CF1830E Bully Sort 做题记录

对于排列 {pi}\{p_i\},定义一次操作为按顺序执行以下步骤:

  1. pip_i 为排列中最大的满足 piip_i\neq i 的数;
  2. pjp_j 为排列中最小的满足 j>ij>i 的数;
  3. 交换 pip_ipjp_j
    定义 f(p)f(p) 为将 pp 升序排序所需的操作次数。

给定一个排列 pp,会进行 qq 次交换 pxi,pyip_{x_i},p_{y_i} 的操作,每次操作后输出 f(p)f(p),交换操作是永久的。

2n5×1052\le n\le 5\times 10^51q5×1041\le q\le 5\times 10^4

人类智慧题。

考虑排序过程中一次操作对逆序对个数 cntcnt 做出的改变,由于 p[i+1,j1]p_{[i+1,j-1]} 均在 [pi+1,pj1][p_i+1,p_j-1] 内,所以一次操作会让逆序对个数减少 2(ji)12(j-i)-1

接下来就很人类智慧。

首先发现 pip_i 这个数在排序过程中只会向它的目的地 pip_i 单向移动。

证明

只需要考虑移动太多导致要反向移动的情况。

首先一次操作中 pjp_j 显然不会移动太多,因为有 pjip_j\le i

证明

j>i,pj>i\forall j>i,p_j>i,则 p[i+1,n]p_{[i+1,n]} 一定构成子排列,那么一定有 pi<ip_i<i

此时找到满足 pk=ip_k=ikk,则一定有 pk=kp_k\not=kpk>pip_k>p_i,与 ii 的定义矛盾。

然后一定有 jpij\le p_i,因为把 pp 的满足 pi=ip_i=i 的极长后缀删掉后 pip_i 一定是最大值。

那么设 sum=k=1nmax(pkk,0)sum=\sum\limits_{k=1}^n\max(p_k-k,0),考虑排序过程中一次操作对这个东西的影响:

  • 一定有 pi>ip_i>i,所以 max(pii,0)=pii\max(p_i-i,0)=p_i-i
  • 一定有 pji<jp_j\le i<j,所以 max(pjj,0)=0\max(p_j-j,0)=0
  • 一定有 pjip_j\le i,所以 max(pji,0)=0\max(p_j-i,0)=0
  • 一定有 pijp_i\ge j,所以 max(pij,0)=pij\max(p_i-j,0)=p_i-j

那么一次操作让 sumsum 减少了:

Δsum=(max(pii,0)+max(pjj,0))(max(pji,0)+max(pij,0))=(pii+0)(0+pij)=ji\begin{aligned} \Delta sum&=(\max(p_i-i,0)+\max(p_j-j,0))-(\max(p_j-i,0)+\max(p_i-j,0))\\ &=(p_i-i+0)-(0+p_i-j)\\ &=j-i \end{aligned}

发现 Δcnt=2(ji)1\Delta cnt=2(j-i)-1,所以 2ΔsumΔcnt=12\Delta sum-\Delta cnt=1

所以 f(p)=2sumcntf(p)=2sum-cnt

sumsum 是很好维护的,cntcnt 的维护相当于三维偏序,可以考虑 cdq 分治。

时间复杂度 O(nlog2n)O(n\log ^2 n),代码如下:

#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;
}