JZOJ6816. 【2020.10.06提高组模拟】随机的排列

49 篇文章 0 订阅
1 篇文章 0 订阅

Description

  • 给你一个长度为 n n n且完全随机的排列 p p p i i i(下标)可以被右边或左边第一个大于它的支配。

  • 现在需要选择最少的点,使得每一个点要么被选择,要么被一个选择的点支配。

  • 还需要支持 q q q次修改:交换相邻的两个随机位置的值。

  • 对于未修改时和每一次修改后输出最少的选择数。

  • n ≤ 1 e 5 , q ≤ 2 e 4 n\le1e5,q\le2e4 n1e5,q2e4

Solution

  • 首先建出笛卡尔树,可以发现一个点支配的点是左儿子的所有右儿子链,和右儿子的所有左儿子链。
  • 所以可以记一个DP, f [ x ] [ 0 / 1 ] [ 0 / 1 ] f[x][0/1][0/1] f[x][0/1][0/1]表示笛卡尔树的 x x x的子树下,左儿子链、右儿子链是否被父亲覆盖, O ( n ) O(n) O(n)处理一次询问。
  • 然后相邻的交换可以直接在树上接一接,断一断,因为完全随机,所以期望的树高的 l o g log log的,由于只会修改两条链上的点,所以暴力改一改就好了。
  • O ( n + q   l o g   n ) O(n+q\ log\ n) O(n+q log n)
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 100005
using namespace std;

int n,q,i,j,k,a[maxn],d[maxn],L[maxn],R[maxn];
int fa[maxn],tr[maxn][2],f[maxn][2][2];

void read(int &x){
	x=0; char ch=getchar();
	for(;ch<'0'||ch>'9';ch=getchar());
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
}

void dp(int x){
	memset(f[x],127,sizeof(f[x]));
	for(int i=0;i<2;i++) for(int j=0;j<2;j++){
		if (i||j) f[x][i][j]=f[tr[x][0]][i][0]+f[tr[x][1]][0][j];
		f[x][i][j]=min(f[x][i][j],f[tr[x][0]][i][1]+f[tr[x][1]][1][j]+1);
	}
}

void change(int v){
	int x,y,z,c; 
	if (a[v]>a[v+1]) x=a[v],y=a[v+1],c=1; 
	else x=a[v+1],y=a[v],c=0;
	swap(a[v],a[v+1]);
	if (fa[y]==x) {
		z=x,tr[x][c]=tr[y][c];
		if (tr[x][c]) fa[tr[x][c]]=x;
	} else {
		z=fa[y],tr[z][c^1]=tr[y][c];
		if (tr[z][c^1]) fa[tr[z][c^1]]=z;
	}
	while (z!=x) dp(z),z=fa[z];
	
	tr[y][0]=tr[y][1]=fa[y]=0;
	if (y>tr[x][c^1]){
		tr[y][c^1]=tr[x][c^1],tr[x][c^1]=y,fa[y]=x;
		if (tr[y][c^1]) fa[tr[y][c^1]]=y;
	} else {
		z=tr[x][c^1];
		while (tr[z][c]>y) z=tr[z][c];
		tr[y][c^1]=tr[z][c],tr[z][c]=y,fa[y]=z;
		if (tr[y][c^1]) fa[tr[y][c^1]]=y;
	}
	z=y;
	while (z) dp(z),z=fa[z];
}

int main(){
	freopen("permutation4.in","r",stdin);
//	freopen("permutation.out","w",stdout);
//	freopen("ceshi.in","r",stdin);
	freopen("ceshi.out","w",stdout);
	read(n),read(q);
	for(i=1;i<=n;i++) read(a[i]);
	for(d[0]=0,i=1;i<=n;i++){
		while (d[0]&&a[d[d[0]]]<a[i]) R[a[d[d[0]]]]=a[i],d[0]--; 
		d[++d[0]]=i;
	}
	for(d[0]=0,i=n;i>=1;i--){
		while (d[0]&&a[d[d[0]]]<a[i]) L[a[d[d[0]]]]=a[i],d[0]--;
		d[++d[0]]=i;
	}
	for(i=1;i<n;i++) if (L[i]&&L[i]<R[i]||!R[i]) fa[i]=L[i],tr[L[i]][1]=i;
		else fa[i]=R[i],tr[R[i]][0]=i;
	for(i=1;i<=n;i++) dp(i);
	printf("%d\n",f[n][0][0]);
	int qi=0;	
	while (q--){
		int x; read(x);
		change(x);
		printf("%d\n",f[n][0][0]);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值