T1 动态逆序对
题目
【题目描述】
给出一个长度为n的排列a(1~n这n个数在数列中各出现1次)。每次交换两个数,求逆序对数%2的结果。
逆序对:对于两个数a[i],a[j](i<j),若a[i]>a[j],则(a[i],a[j])为1个逆序对。
【输入格式】
第一行一个正整数n。
接下来一行n个数,表示给出的排列a。
接下来一行一个正整数q。
接下来q行,每行两个正整数i,j,表示交换a[i]和a[j]。
【输出格式】
输出共q行,表示每次交换后的逆序对数%2的结果。
【输入样例】
4 1 2 3 4 2 1 2 1 2
【输出样例】
1 0
【数据规模】
对于60%的数据:n,q≤100;
对于80%的数据:n,q≤1000;
对于100%的数据:n,q≤100000。
解析
先求出初始序列的逆序对总数对2取余的结果。
每次交换a[i]与a[j](i<j),对于a[k]的影响如下:
- 若k<i,a[k]依旧在a[i]与a[j]前面,所以a[k]与a[i]、a[j]产生的逆序对数不变;
- 若k>j,同上,逆序对数不变;
- 若i<k<j,如果a[i]<a[k],则逆序对数+1,否则-1,;如果a[j]>a[k],则逆序对数+1,否则-1,
而我们只需求出逆序对数对2取余的结果,可以发现,逆序对个数的奇偶性与k无关。
事实上,只需在每次交换位置时,令逆序对总数对2取余的结果^1即可(i=j时则不变)。
Code
#include <algorithm> #include <iostream> #include <cstring> #include <string> #include <cstdio> #include <cmath> using namespace std; inline int read() { int num=0,w=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') w=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { num=(num<<1)+(num<<3)+ch-'0'; ch=getchar(); } return num*w; } int n,q,a[100100],f[100100],temp; void add(int x,int y) { for(;x<=n;x+=(x&-x)) f[x]+=y; } int ask(int x) { int ans=0; for(;x;x-=(x&-x)) ans+=f[x]; return ans; } int main() { //freopen("lyk.in","r",stdin); //freopen("lyk.out","w",stdout); n=read(); for(int i=1;i<=n;i++) a[i]=read(); q=read(); for(int i=n;i>=1;i--) { temp+=ask(a[i]-1); add(a[i],1); } temp&=1; for(int i=1;i<=q;i++) { int x=read(),y=read(); if(x!=y) temp^=1; cout<<temp<<endl; } return 0; }