CF785E Anton and Permutation
带交换逆序对。
前面(位置)比它大的数的个数+后面比它小的个数,很显然这就是一个三维偏序的问题(时间上也要满足偏序)
首先把第一维时间排序(原本就有序了)
然后对位置进行CDQ分治,对右半部分计算左半部分:位置在其左边值大,位置在其右边值小的方案
注意:左右两半部分在位置上是独立有序的,在计算时还要看另一边是否满足位置上的限制
这样就能得到当前区间,每一个操作对应的逆序对,如果是插入,对应答案就加上,如果是删除,就剪掉
注意上面的这一个过程,其实只是单独考虑每个操作对答案的影响,最后还要把答案求个前缀和
cdq分治题解代码:
view code
#include <iostream>
#include <cstdio>
#define ll long long
using namespace std;
const int N=2e5+10,M=5e4+10;
int n;
int val[N],t[N];
ll ans[M];
struct node
{
int a,b,c;
int type,id;
}v[N+4*M],w[N+4*M];
inline int lowbit(int x)
{
return x&(-x);
}
inline void change(int x,int c)
{
for(int i=x;i<=n;i+=lowbit(i))
t[i]+=c;
}
inline int query(int x)
{
int res=0;
for(int i=x;i>=1;i-=lowbit(i))
res+=t[i];
return res;
}
inline void CDQ(int l,int r)
{
if(l==r) return;
int mid=(l+r)/2,p,q;
CDQ(l,mid),CDQ(mid+1,r);
p=l,q=mid+1;
while(q<=r)//计算左边位置小值大
{
while(p<=mid&&v[p].b<=v[q].b) change(v[p].c,v[p].type),p++;
ans[v[q].id]+=v[q].type*(query(n)-query(v[q].c)),q++;
}
for(int i=l;i<p;i++) change(v[i].c,-v[i].type);
p=mid,q=r;
while(q>mid)//计算左边位置大值小
{
while(p>=l&&v[p].b>=v[q].b) change(v[p].c,v[p].type),p--;
ans[v[q].id]+=v[q].type*query(v[q].c-1),q--;
}
for(int i=mid;i>p;i--) change(v[i].c,-v[i].type);
p=l,q=mid+1;
for(int i=l;i<=r;i++)//归并排序
{
if((p<=mid&&v[p].b<=v[q].b)||q>r) w[i]=v[p++];
else w[i]=v[q++];
}
for(int i=l;i<=r;i++) v[i]=w[i];
}
int main()
{
int m,tot=0,a,b;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
val[i]=i,v[++tot]=(node){tot,i,i,1,0};
for(int i=1;i<=m;i++)
{
scanf("%d%d",&a,&b);
v[++tot]=(node){tot,a,val[a],-1,i};
v[++tot]=(node){tot,b,val[b],-1,i};
v[++tot]=(node){tot,a,val[b],1,i};
v[++tot]=(node){tot,b,val[a],1,i};
swap(val[a],val[b]);
}
CDQ(1,tot);
for(int i=1;i<=m;i++)
ans[i]+=ans[i-1];
for(int i=1;i<=m;i++)
printf("%lld
",ans[i]);
return 0;
}