【题解】NOIO2020 冒泡排序(树状数组)
考虑在k次冒泡后的排列中的一个逆序对(i<j,a_i>a_j)。
因为这个(a_i)的存在,意味着(a_j)没有被往右边交换过(否则不会存在一个(a_i>a_j))。
对于每个没有被交换到右边的数,他前面总共有(k)个比他大的数被交换走了。记(ans[i])表示原序列(i)位置和之前的数构成的逆序对个数,那么(k)次交换后,对答案的贡献是(ans[i]-k)。
那么答案就是
[sum max(0, ext{ans}[i]-k)
]
对(ans[i])的值域开两个树状数组,每次取所有大于(k)的所有(ans)之和,再减去(ans[i]>k)的个数乘以(k)就是答案。两个操作都是取一段后缀,直接树状数组即可。
至于交换操作,由于只影响了两个(ans),所以直接模拟一遍就行。
复杂度(O(nlog n))。注意(k)要对(n-1)取min。
//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std; typedef long long ll;
inline int qr(){
int ret=0,f=0,c=getchar();
while(!isdigit(c)) f|=c==45,c=getchar();
while( isdigit(c)) ret=ret*10+c-48,c=getchar();
return f?-ret:ret;
}
const int maxn=2e5+5;
int data[maxn],n,m;
ll seg1[maxn],seg2[maxn];
void add(ll*seg,int pos,ll val){
++pos;
for(int t=pos;t<=n+1;t+=t&-t)
seg[t]+=val;
}
ll que(ll*seg,int pos){
++pos;
ll ret=0;
for(int t=pos;t>0;t-=t&-t)
ret+=seg[t];
return ret;
}
ll que(ll*seg,int l,int r){return que(seg,r)-que(seg,l-1);}
int ans[maxn];
void del(int pos){add(seg1,ans[pos],-ans[pos]); add(seg2,ans[pos],-1);}
void add(int pos){add(seg1,ans[pos],ans[pos]); add(seg2,ans[pos],1);}
int main(){
n=qr(); m=qr();
for(int t=1;t<=n;++t)
data[t]=qr(),ans[t]=t-1-que(seg1,data[t]),add(seg1,data[t],1);
memset(seg1,0,sizeof seg1);
for(int t=1;t<=n;++t) add(t);
for(int t=1;t<=m;++t){
int op=qr(),x=min(qr(),n);
if(op==1){
del(x),del(x+1);
ans[x+1]-=data[x]>data[x+1];
swap(data[x],data[x+1]); swap(ans[x],ans[x+1]);
ans[x+1]+=data[x]>data[x+1];
add(x),add(x+1);
}else{
ll ret=que(seg1,x,n)-que(seg2,x,n)*x;
printf("%lld
",ret);
}
}
return 0;
}