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 2∗num肯定是个偶数,那么我们只要看后面一项的奇偶性就可以判断最终整体的奇偶性了,如果(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;
}