对于排列 ,定义一次操作为按顺序执行以下步骤:
- 令 为排列中最大的满足 的数;
- 令 为排列中最小的满足 的数;
- 交换 和 ;
定义 为将 升序排序所需的操作次数。给定一个排列 ,会进行 次交换 的操作,每次操作后输出 ,交换操作是永久的。
,。
人类智慧题。
考虑排序过程中一次操作对逆序对个数 做出的改变,由于 均在 内,所以一次操作会让逆序对个数减少 。
接下来就很人类智慧。
首先发现 这个数在排序过程中只会向它的目的地 单向移动。
证明
只需要考虑移动太多导致要反向移动的情况。
首先一次操作中 显然不会移动太多,因为有 。
证明
若 ,则 一定构成子排列,那么一定有 。
此时找到满足 的 ,则一定有 且 ,与 的定义矛盾。
然后一定有 ,因为把 的满足 的极长后缀删掉后 一定是最大值。
那么设 ,考虑排序过程中一次操作对这个东西的影响:
- 一定有 ,所以 ;
- 一定有 ,所以 ;
- 一定有 ,所以 ;
- 一定有 ,所以 ;
那么一次操作让 减少了:
发现 ,所以 。
所以 。
是很好维护的, 的维护相当于三维偏序,可以考虑 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;
}
