题目描述
你一开始有一个长度为 n n n的升序排列 a a a。
有 q q q次询问,每次询问都会交换两个数 a l a_l al和 a r a_r ar,并求交换后整个排列顺序对的数量。
本题强制在线。
输入格式
第一行两个整数 n , q n,q n,q。
接下来 q q q行,每行两个整数 l , r l,r l,r,表示一次询问。输入是加密的,解密方法如下:
令上次询问的结果为 x x x(如果这是第一次询问,则 x = 0 x=0 x=0),你需要将 l , r l,r l,r分别异或上 x x x,对 n n n取模后再加 1 1 1,两者的较小值为真实的 l l l,较大值为真实的 r r r。
操作有后效性,即前面操作到后面仍不还原。
输出格式
一共 q q q,行,每行一个整数表示答案。
样例输入
5 3
1 3
4 2
1 5
样例输出
7
6
7
数据范围
1 ≤ n ≤ 1 0 5 , 1 ≤ q ≤ 1 0 5 1\leq n\leq 10^5,1\leq q\leq 10^5 1≤n≤105,1≤q≤105
题解
显然, a l a_l al和 a r a_r ar交换后,受影响的只有区间 [ l + 1 , r − 1 ] [l+1,r-1] [l+1,r−1]。对于区间 [ l + 1 , r − 1 ] [l+1,r-1] [l+1,r−1],设区间长为 l e n len len,分如下情况考虑
- 对于 a l a_l al,区间中大于 a l a_l al的与 a l a_l al构成顺序对,交换后构成逆序对,应减去;区间中小于 a l a_l al的与 a l a_l al构成逆序对,交换后构成顺序对,应加上
- 对于 a r a_r ar,区间中小于 a r a_r ar的与 a r a_r ar构成顺序对,交换后构成逆序对,应减去;区间中大于 a r a_r ar的与 a r a_r ar构成逆序对,交换后构成顺序对,应加上
- 还要考虑 a l a_l al和 a r a_r ar交换后两个数对答案的影响。如果 a l < a r a_l<a_r al<ar,则答案减一;如果 a l > a r a_l>a_r al>ar,则答案加一
设区间长为 l e n len len,区间中大于 a l a_l al的数为 p p p,大于 a r a_r ar的数为 q q q,则交换后,区间 [ l + 1 , r − 1 ] [l+1,r-1] [l+1,r−1]对答案的影响为 ( l e n − p ) − p + q − ( l e n − q ) (len-p)-p+q-(len-q) (len−p)−p+q−(len−q)。
我们可以用分块来维护序列,用一个数组来存储每一块排序后的序列。对于一次查询,整块的可以二分查询大于 x x x的数,而非整块的可以暴力求出。交换 a l a_l al和 a r a_r ar可以用插入排序分别修改两个数对应的块。
区间查询的时间复杂度为 O ( n log n ) O(\sqrt n\log n) O(nlogn),区间修改的时间复杂度为 O ( n ) O(\sqrt n) O(n),总时间复杂度为 O ( n n log n ) O(n\sqrt n\log n) O(nnlogn)。
code
#include<bits/stdc++.h>
using namespace std;
int n,q,bl,a[100005],b[100005];
long long lst=0,ans=0;
int dd(int x,int l,int r){
if(l>r) return 0;
int re=0;
int vl=(l-1)/bl+1,vr=(r-1)/bl+1;
if(vl==vr){
for(int i=l;i<=r;i++){
if(a[i]>x) ++re;
else --re;
}
return re;
}
for(int i=l;i<=vl*bl;i++){
if(a[i]>x) ++re;
else --re;
}
for(int i=vr*bl-bl+1;i<=r;i++){
if(a[i]>x) ++re;
else --re;
}
for(int i=vl+1;i<=vr-1;i++){
int t;
if(b[i*bl]<x) t=bl;
else t=upper_bound(b+i*bl-bl+1,b+i*bl+1,x)-b-(i-1)*bl-1;
re+=bl-t-t;
}
return re;
}
void pt(int x,int v1,int v2){
int w,vl=x*bl-bl+1,vr=min(x*bl,n);
for(int i=vl;i<=vr;i++){
if(b[i]==v1){
w=i;
b[i]=v2;
break;
}
}
int p=w;
while(p-1>=vl&&b[p-1]>b[p]){
swap(b[p-1],b[p]);
--p;
}
p=w;
while(p+1<=vr&&b[p+1]<b[p]){
swap(b[p],b[p+1]);
++p;
}
}
int main()
{
scanf("%d%d",&n,&q);
bl=sqrt(n);
for(int i=1;i<=n;i++){
a[i]=b[i]=i;
}
ans=1ll*n*(n-1)/2;
while(q--){
int l,r;
scanf("%d%d",&l,&r);
l=(l+lst+n-1)%n+1;
r=(r+lst+n-1)%n+1;
if(l>r) swap(l,r);
ans=ans-dd(a[l],l+1,r-1)+dd(a[r],l+1,r-1);
if(a[l]>a[r]) ++ans;
else if(a[l]<a[r]) --ans;
pt((l-1)/bl+1,a[l],a[r]);
pt((r-1)/bl+1,a[r],a[l]);
swap(a[l],a[r]);
printf("%lld\n",ans);
lst=ans%n;
}
return 0;
}