题解 P6186 [NOI Online 提高组]冒泡排序
前言:这里有一只考场上以为打了20分部分分民间数据只有10分赛后100分的蒟蒻。
题目传送门:P6186 [NOI Online 提高组]冒泡排序
解题思路:
我们要做出这道题目,首先要发现冒泡排序的本质,我们知道冒泡排序外层循环最多跑
例子:4 3 5 2 1
那么这个序列进行一次冒泡之后是这个样子的。
第一轮后:3 4 2 1 5
我们发现,4与3交换,5与2与1交换,但2却没有和1交换,所以一个数可以同自己右边的数进行交换当且仅当左边没有比它大的数,形象化为当你的左手边没有比你更强的人碾压你,你就可以一路向右碾压,直到碰到一个比你强的人,我们将此类数的状态称为可以碾压别人,非此类数称为不可碾压别人,这个就是冒泡排序的本质。
本质弄清了,我们还要处理的是,每次冒泡会减少多少逆序对,如果当前有
所以当一个数前面有
用树状数组算出从未开始(第
进行交换的话如何修改?我们设数组
如果
考虑这个序列:2 1 3
交换后面两个元素:2 3 1
当序列为:1 2 3的时候逆序对个数恢复原状。
如果
考虑这个序列:2 3 1
交换后面两个元素:2 1 3
当序列为:1 2 3的时候逆序对个数恢复原状。
也就是说比较交换前后的
最后利用差分思想查询即可。
如果看一遍看不懂,请细品。
如果还是看不懂,建议思考交换后
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
int n,m,a[maxn],b[maxn],d[maxn];
long long c[maxn],ans;
inline int lowbit(int x){
return x&(-x);
}
inline void update(int x,long long val){
while(x<=n){
c[x]+=val;
x+=lowbit(x);
}
}
inline long long getsum(int x){
long long res=0;
while(x>0){
res+=c[x];
x-=lowbit(x);
}
return res;
}
int main(){
int opt,x,tmp=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
b[i]=i-1-getsum(a[i]);
ans+=b[i],++d[b[i]];
update(a[i],1);
}
memset(c,0,sizeof(c));
update(1,ans);
for(int i=0;i<n;++i){
tmp+=d[i];
update(i+2,-(n-tmp));
}
for(int i=1;i<=m;++i){
scanf("%d%d",&opt,&x);
x=min(x,n-1);
if(opt==1){
if(a[x]<a[x+1]){
swap(a[x],a[x+1]);
swap(b[x],b[x+1]);
update(1,1);
update(b[x+1]+2,-1);
b[x+1]++;
}
else{
swap(a[x],a[x+1]);
swap(b[x],b[x+1]);
update(1,-1);
b[x]--;
update(b[x]+2,1);
}
}
else printf("%lld\n",getsum(x+1));
}
return 0;
}