对于排列 ,定义一次操作为按顺序执行以下步骤:
- 令 为排列中最大的满足 的数;
- 令 为排列中最小的满足 的数;
- 交换 和 ;
定义 为将 升序排序所需的操作次数。给定一个排列 ,会进行 次交换 的操作,每次操作后输出 ,交换操作是永久的。
,。
人类智慧题。
考虑排序过程中一次操作对逆序对个数 做出的改变,由于 均在 内,所以一次操作会让逆序对个数减少 。
接下来就很人类智慧。
首先发现 这个数在排序过程中只会向它的目的地 单向移动。
证明
只需要考虑移动太多导致要反向移动的情况。
首先一次操作中 显然不会移动太多,因为有 。
证明
若 ,则 一定构成子排列,那么一定有 。
此时找到满足 的 ,则一定有 且 ,与 的定义矛盾。
然后一定有 ,因为把 的满足 的极长后缀删掉后 一定是最大值。
那么设 ,考虑排序过程中一次操作对这个东西的影响:
- 一定有 ,所以 ;
- 一定有 ,所以 ;
- 一定有 ,所以 ;
- 一定有 ,所以 ;
那么一次操作让 减少了:
发现 ,所以 。
所以 。
是很好维护的, 的维护相当于三维偏序,可以考虑 cdq 分治。
时间复杂度 ,代码如下:
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
struct node
{
int tme,pos,val,w;
};
const int S=500005;
int n,q,a[S];
int tot;
node que[S*2];
int c[S];
long long res[S],ans[S];
inline void addt(int p,int val)
{
for(int i=p;i<=n;i+=i&-i) c[i]+=val;
}
inline int quet(int p)
{
int res=0;
for(int i=p;i>=1;i-=i&-i) res+=c[i];
return res;
}
inline void clrt(int p)
{
for(int i=p;i<=n;i+=i&-i) c[i]=0;
}
void cdq(int l,int r)
{
if(l>=r) return;
int mid=l+r>>1;
cdq(l,mid);
cdq(mid+1,r);
auto cmp=[&](node x,node y){return x.pos<y.pos;};
sort(que+l,que+mid+1,cmp),sort(que+mid+1,que+r+1,cmp);
int p=l;
for(int i=mid+1;i<=r;i++)
{
while(p<=mid&&que[p].pos<que[i].pos) addt(n-que[p].val+1,que[p].w),p++;
ans[que[i].tme]+=quet(n-que[i].val)*que[i].w;
}
for(int i=l;i<p;i++) clrt(n-que[i].val+1);
p=mid;
for(int i=r;i>=mid+1;i--)
{
while(p>=l&&que[p].pos>que[i].pos) addt(que[p].val,que[p].w),p--;
ans[que[i].tme]+=quet(que[i].val-1)*que[i].w;
}
for(int i=p+1;i<=mid;i++) clrt(que[i].val);
}
int main()
{
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
que[++tot]=(node){0,i,a[i],true};
res[0]+=max(a[i]-i,0);
}
for(int i=1;i<=q;i++)
{
int x,y;
scanf("%d%d",&x,&y);
que[++tot]=(node){i*2-1,x,a[x],-1};
que[++tot]=(node){i*2-1,x,a[y],1};
que[++tot]=(node){i*2,y,a[y],-1};
que[++tot]=(node){i*2,y,a[x],1};
res[i]=res[i-1];
res[i]-=max(a[x]-x,0);
res[i]-=max(a[y]-y,0);
swap(a[x],a[y]);
res[i]+=max(a[x]-x,0);
res[i]+=max(a[y]-y,0);
}
cdq(1,tot);
for(int i=1;i<=q*2;i++) ans[i]+=ans[i-1];
for(int i=1;i<=q;i++) printf("%lld\n",2*res[i]-ans[i*2]);
return 0;
}