【SSLOJ 3347】動態逆序對

SSL_LMZ發表於2024-11-16

題目大意

給出一個長度為 \(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;
}

相關文章