Codeforces 911D Inversion Counting

Codeforces 911D Inversion Counting

题目传送门

题目大意

给出一个含有n个数的序列,求每次反转区间 [l,r] 后整个序列的逆序数是奇数个还是偶数个

思路

首先纯模拟肯定是不行的,不然是D题也太蠢了,然后要注意到[l,r]反转这个操作是有封闭性的,因为反转[l,r]并不影响到它前面数和后面数与[l,r]中的逆序对关系,举个例子:a[l-1]与a[l]是一对逆序对,a[l]与a[r+1]也是一对逆序对,a[l]反转以后,a[l]的位置依然处于a[l-1]和a[r+1]之间,他们依旧是逆序对关系!

还有一点,原来是逆序对的在反转以后变成了正序对,而原来是正序对的反转以后就变成了逆序对。

举个例子:1 3 4 2 5 这五个数中原来是逆序对的是[3,2]、[4,2],那么我们把整个序列反转过来以后会发现,序列变成了:5 2 4 3 1,可以发现这里的正序对只有[2,4],剩下的都是逆序对。

所以逆序对变化的数量就是: 2 * num - (len * (len - 1))/2,其中num是原来序列中逆序对的数量,(这个式子怎么得到的很好推,这里就不细说了)

由于 2 ∗ n u m 2*num 2num肯定是个偶数,那么我们只要看后面一项的奇偶性就可以判断最终整体的奇偶性了,如果(len*(len-1))/2是奇数,那么整体的奇偶性肯定变,反之不变

代码

#include <bits/stdc++.h>
#define LL long long
using namespace std;

int m, n;
int l, r;
int t, x, y;
LL cnt = 0, sum;
int tmp[1505];
int a[1505];

void merge(int left, int right, int mid) {
    int middle[1505];
    int i = left, j = mid + 1;
    int k = left;
    while (i <= mid && j <= right) {
        if (tmp[i] > tmp[j]) {
            middle[k++] = tmp[i];
            i++;
            sum += (right - j + 1);
        } else {
            middle[k++] = tmp[j];
            j++;
        }
    }
    if (i <= mid) {
        while (i <= mid) {
            middle[k++] = tmp[i++];
        }
    } else {
        while (j <= right) {
            middle[k++] = tmp[j++];
        }
    }
    for (int i = left; i <= right; i++)
        tmp[i] = middle[i];
}

void find(int left, int right) {
    if (left < right) {
        int mid = (left + right) / 2;
        find(left, mid);
        find(mid + 1, right);
        merge(left, right, mid);
    }
}


int main() {
    ios::sync_with_stdio(false);
    cin >> n;
    for(int i = 1; i <= n; ++i)
        cin >> a[i];
    cin >> t;
    cnt = 0;
    for(int i=1;i<=n;++i)
        tmp[i] = a[i];
    find(1, n);
    cnt = sum;//先求一次整个的逆序对
    bool judge = cnt & 1;
    while (t--) {
        cin >> x >> y;
        int len = y - x + 1;
        if(len*(len-1)/2 & 1)
            judge = !judge;
        if(judge)
            cout<<"odd"<<endl;
        else
            cout<<"even"<<endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值