一道关于逆序对的题目

逆序对

题目描述

Smart得到了一个1~n的全排列。
Smart每次会交换第i个数和第j个数,对于每一次交换,Smart需要Sarah回答该全排列的逆序对数为多少。
“1、2、3、4、………、248289469!”Sarah如是回答到。
Smart觉得答案数太大,不太好判断是否正确,所以只需回答最后答案取模2的结果。

输入格式  1787.in

第一行一个整数n;
第二行为1~n的某个全排列;
第三行一个整数m,表示交换操作的次数。
接下来m行,每行两个数i和j。

输出格式  1787.out

输出m行,每行包含一个整数表示交换后答案。

输入样例  1787.in
1
1 2 3 4
1
1 2
输出样例  1787.out

1
【数据范围】
30%的数据:n,m≤1000;
100%的数据:n,m≤100000。

一开始想的做法是树状数组骗分。然后发现答案是这样的:……0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1……。

然后试了试把先求出第一个答案,然后不断取反(亦或1)好像还intersting的ac了。

后来证明了一下:

每次交换排列中两个不相等的数,必会增加或减少奇数对逆序对。证明:

设被交换的数分别为 a[i] 和 a[j],且 i < j。交换前逆序对数为 x。于是有两种情况:a[i]>a[j] 或 a[i]

情况一:a[i]>a[j]为了方便,我们不妨将 n 个数按下标分为三部分,[1,i) 为A部分,(i,j) 为B部分,(j,n] 为C部分,如图。(注意 i 和 j 不属于任何一部分,因为它们是待交换的)

首先,A、B、C 每个部分数中(不含 i 和 j!)各自的逆序对数不受交换影响,这是显然的。

在 A 部分中的数,交换后对逆序对数没有影响。因为虽然 a[i] 和 a[j] 的绝对位置交换了,但是它们相对于 A 部分中的数的相对位置依然是不变的。同理,在 C 部分中的数,交换后对逆序对数也没有影响。

于是我们就要考虑 B 部分中的数,分三类讨论:

大于 a[i] 的数,必然也大于 a[j],不妨设为 x,易知交换前有逆序对 (x, a[j]),交换后有逆序对 (x, a[i]),故它们对逆序对数无影响。

小于 a[j] 的数,必然也小于 a[i],不妨设为 y,易知交换前有逆序对 (a[i], y),交换后有逆序对 (a[j], y),故它们对逆序对数无影响。

小于 a[i] 而大于 a[j] 的数。不妨设为 z,易知交换前有逆序对 (a[i], z) 和 (z, a[j]),交换后无逆序对。若这样的数有 p 个,则逆序对数减少 2p 对。

最后,因为 a[i]>a[j],所以交换前有逆序对 (a[i],a[j]),交换后无逆序对。故,总共减少了 2p+1 对逆序对,显然减少的是奇数。

情况二:a[i]

于是可以得知,当交换排列中两个不相等数时,逆序对数会增加或减少奇数对,则对 2 取模的值会从 0 变 1,或从 1 变 0。但要注意此题可能交换 (i, i) 之类的,所以要判断一下,如果交换的是同一个数,答案与之前一致。

//以上证明源于:http://blog.csdn.net/u013686535 博主本人

code:


#include
#include
#include
#include
using namespace std;
#define Maxn 100010
#define lowbit(x) x&(-x)
int a[Maxn];
int dp[Maxn<<2];
int n,ans=0;
int sum(int x)
{
int t=0;
while(x>0)
{
t+=dp[x];
x-=lowbit(x);
}
return t;
}
void insert(int x,int d)
{
dp[x]+=d;
while(x<=n)
{
x+=lowbit(x);
dp[x]+=d;
}
return;
}


int main()
{
freopen("1787.in","r",stdin);
freopen("1787.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++) 
{
scanf("%d",&a[i]);
}
int m,x,y;
scanf("%d",&m);
scanf("%d%d",&x,&y);
swap(a[x],a[y]);
memset(dp,0,sizeof(dp));
ans = 0;
for(int i=n;i>=1;i--)
{
ans=(ans+sum(a[i]-1))%2;
insert(a[i],1);
}
ans%=2; printf("%d\n",ans%2);


for(int i=1;i{
scanf("%d%d",&x,&y);
if(x==y) printf("%d\n",ans);
else{
ans=(ans+1)%2;
printf("%d\n",ans);
}
}
return 0;
}


你可能感兴趣的:(日常水)