链接:点击打开链接
题意:给出一个1~n的序列,有m次变换,每次交换两个元素的位置,求每次交换后的逆序数
代码:
#include <math.h>
#include <vector>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
using namespace std;
vector<int> G[10005];
int n,m,siz,a[200005];
int le[200005],re[200005],bel[200005];
void init(){ //bel[i]为i属于第几块
int i,id,tmp; //le[i]为第i块左端点的位置
tmp=sqrt(n); //re[i]为第i块右端点的位置
siz=n/tmp; //G[i]维护第i块内的元素
if(n%tmp)
siz++;
for(i=0;i<=siz;i++)
G[i].clear();
for(i=1;i<=n;i++){
a[i]=i;
id=(i-1)/tmp+1;
bel[i]=id;
G[id].push_back(i);
}
for(i=1;i<=siz;i++)
le[i]=(i-1)*tmp+1,re[i]=i*tmp;
re[siz]=n;
}
void update(int u,int v){ //维护每个快内有序,从而才能在查询时二分
int idu,idv;
idu=bel[u],idv=bel[v];
G[idu].erase(lower_bound(G[idu].begin(),G[idu].end(),a[u]));
G[idu].insert(upper_bound(G[idu].begin(),G[idu].end(),a[v]),a[v]);
G[idv].erase(lower_bound(G[idv].begin(),G[idv].end(),a[v]));
G[idv].insert(upper_bound(G[idv].begin(),G[idv].end(),a[u]),a[u]);
swap(a[u],a[v]);
}
int query(int ll,int rr,int x){
if(ll>rr)
return 0;
int i,j,ans,idl,idr;
ans=0;
idl=bel[ll],idr=bel[rr];
if(idl==idr){ //同块直接暴力找
for(i=ll;i<=rr;i++)
if(a[i]>x)
ans++;
return ans;
}
for(i=ll;i<=re[idl];i++) //边界也是暴力找
if(a[i]>x)
ans++;
for(i=idl+1;i<=idr-1;i++) //因为每块单调上升所以每块内直接二分
ans+=(G[i].size()-(lower_bound(G[i].begin(),G[i].end(),x)-G[i].begin()));
for(i=le[idr];i<=rr;i++)
if(a[i]>x)
ans++;
return ans;
}
int main(){ //分块后,边界直接暴力,快内二分
long long ans; //复杂度又O(m*n*n)就变成了O(m*sqrt(n)*log(sqrt(n)))
int i,j,u,v,tmp;
while(scanf("%d%d",&n,&m)!=EOF){
ans=0,init();
while(m--){
scanf("%d%d",&u,&v);
if(u==v){
printf("%I64d\n",ans);
continue;
}
if(u>v)
swap(u,v);
tmp=query(u+1,v-1,a[u]); //分别找出区间内大于a[u]和a[v]的数
ans+=tmp; //再根据逆序数的定义推出变换后的值
ans-=(v-u-1-tmp);
tmp=query(u+1,v-1,a[v]);
ans-=tmp;
ans+=(v-u-1-tmp);
if(a[u]>a[v])
ans--;
else
ans++;
printf("%I64d\n",ans);
update(u,v); //查询完更新
}
}
return 0;
}