【题解】NOIO2020 冒泡排序(树状数组)
【题解】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,\text{ans}[i]-k)
\]
对\(ans[i]\)的值域开两个树状数组,每次取所有大于\(k\)的所有\(ans\)之和,再减去\(ans[i]>k\)的个数乘以\(k\)就是答案。两个操作都是取一段后缀,直接树状数组即可。
至于交换操作,由于只影响了两个\(ans\),所以直接模拟一遍就行。
复杂度\(O(n\log 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\n",ret);
}
}
return 0;
}
博客保留所有权利,谢绝学步园、码迷等不在文首明显处显著标明转载来源的任何个人或组织进行转载!其他文明转载授权且欢迎!