題目大意
給出一個長度為 \(n\) 的排列 \(a\)。每次交換兩個數,求逆序對數對 \(2\) 取模的結果。
輸入格式
第一行一個正整數\(n\) 。
第二行 \(n\)個數,表示給出的排列 \(a\)。
第三行一個正整數\(q\) 。
接下來 \(q\)行,每行兩個正整數 ,表示交換 \(a_i\)和 \(a_j\)。
輸出格式
輸出共 \(q\)行,表示每次交換後逆序對數對 \(2\)取模的結果。
樣例
【輸入1樣例】
4
1 2 3 4
2
1 2
1 2
【輸出1樣例】
1
0
【輸入2樣例】
8
4 1 5 2 6 8 7 3
10
6 4
7 8
2 2
1 1
7 7
1 7
3 3
2 4
2 6
5 7
【輸出2樣例】
0
1
1
1
1
0
0
1
0
1
對於\(100\%\)的資料,\(n,q\le100000\)
基本思路
首先我們肯定要求修改前的逆序對數,那就要用到樹狀陣列。基本原理是先從大到小將所有數排好,再逐個按位置順序裝進樹狀陣列裡,每裝進一個就統計它前面有幾個數,因為先於它裝進去肯定會比它大。但是我們要注意相同大小的數是不能計算逆序對的,所以對於相同的數還要按位置從大到小排。
在此之前我們需要注意到題目只要我們給出奇偶性,那麼事出反常必有因,這很可能是個結論題,大的方向就是哪些因素會影響逆序對數的奇偶性。
那麼對於詢問修改的如何統計呢?首先我們要明白若以交換的兩個數之間為區間,那麼此區間外的逆序對數是不會改變的,因為相對位置不變。
那麼我們來考慮區間裡面,首先對於兩個交換的數肯定會對改變奇偶性產生 \(1\) 的貢獻的,因為只要兩個數不同交換就會產生加減 \(1\) 的改變。那麼對於區間裡面的數呢?比\(a_i\)和\(a_j\)都大或都小肯定產生不了貢獻。如果夾在它們兩個中間呢?那麼我們可以輕易得出貢獻是加減 \(2\) ,對奇偶性沒影響。最終得出結論:只要交換的兩個數不相等那麼就改變奇偶性。(但在實踐上好像只需要位置不同就可以了)
核心程式碼
#include <bits/stdc++.h>
using namespace std;
#define num first
#define pos second
typedef long long ll;
typedef pair<int,int> pii;
const int N=1e5+10;
int n,a[N],q,sum[N];
ll ans;
pii cnt[N];
bool compare(pii nx,pii ny){
if(nx.num==ny.num) return nx.pos>ny.pos;
return nx.num>ny.num;
}
int query(int x){
int ret=0;
for(;x;x-=(x&(-x)))
ret+=sum[x];
return ret;
}
int add(int x){
int ret=query(x-1);
for(;x<=n;x+=(x&(-x)))
sum[x]++;
return ret;
}
int main(){
freopen("lyk.in","r",stdin);
freopen("lyk.out","w",stdout);
ios::sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
cnt[i].num=a[i];
cnt[i].pos=i;
}
sort(cnt+1,cnt+1+n,compare);
for(int i=1;i<=n;i++)
ans+=add(cnt[i].pos);
ans=ans&1;
cin>>q;
for(register int i=1,u,v;i<=q;i++){
cin>>u>>v;
if(a[u]!=a[v]) ans=!ans;
cout<<ans<<endl;
}
return 0;
}