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

    科技2023-10-31  122

    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]); } }
    Processed: 0.029, SQL: 9