没有上司的舞会

本文介绍了一种利用动态规划和平衡树解决动态加子节点树的最大独立集问题的方法。通过维护平衡树上的状态,实现了高效更新和查询。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意

动态加子节点的树,每次询问最大独立集大小。

DP

给平衡树每个点x维护一个f[x,0/1,0/1]表示以x为根的这个平衡树区间在原树上对应的部分(包括连出去的虚子树)的最大独立集是多少,且这个平衡树区间的左端和右端选或不选。
然后就很好做了。

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=300000+10,inf=1000000000;
int f[maxn][2][2],g[maxn][2],sta[maxn];
int father[maxn],tree[maxn][2],pp[maxn];
bool bz[maxn];
int i,j,k,l,t,n,m,tot,top,ans;
bool czy;
int read(){
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
int pd(int x){
    return tree[father[x]][1]==x;
}
void updata(int &x,int y){
    x=max(x,y);
}
void update(int x){
    f[x][0][0]=f[x][0][1]=-inf;
    f[x][1][0]=f[x][1][1]=-inf;
    int i,j,l=tree[x][0],r=tree[x][1];
    if (!l&&!r){
        updata(f[x][0][0],g[x][0]);
        updata(f[x][1][1],g[x][1]+1);
    }
    else if (!l){
        fo(i,0,1){
            updata(f[x][0][i],f[r][0][i]+g[x][0]);
            updata(f[x][0][i],f[r][1][i]+g[x][0]);
            updata(f[x][1][i],f[r][0][i]+g[x][1]+1);
        }
    }
    else if (!r){
        fo(i,0,1){
            updata(f[x][i][0],f[l][i][0]+g[x][0]);
            updata(f[x][i][0],f[l][i][1]+g[x][0]);
            updata(f[x][i][1],f[l][i][0]+g[x][1]+1);
        }
    }
    else{
        fo(i,0,1)
            fo(j,0,1){
                updata(f[x][i][j],f[l][i][0]+f[r][0][j]+g[x][0]);
                updata(f[x][i][j],f[l][i][0]+f[r][0][j]+g[x][1]+1);
                updata(f[x][i][j],f[l][i][1]+f[r][0][j]+g[x][0]);
                updata(f[x][i][j],f[l][i][0]+f[r][1][j]+g[x][0]);
                updata(f[x][i][j],f[l][i][1]+f[r][1][j]+g[x][0]);
            }
    }
}
void rotate(int x){
    int y=father[x],z=pd(x);
    father[x]=father[y];
    if (father[y]) tree[father[y]][pd(y)]=x;
    tree[y][z]=tree[x][1-z];
    if (tree[x][1-z]) father[tree[x][1-z]]=y;
    tree[x][1-z]=y;
    father[y]=x;
    update(y);
    update(x);
    if (pp[y]) pp[x]=pp[y],pp[y]=0;
}
void splay(int x,int y){
    while (father[x]!=y){
        if (father[father[x]]!=y)
            if (pd(father[x])==pd(x)) rotate(father[x]);else rotate(x);
        rotate(x);
    }
}
void real_empty(int x,int z){
    int i,j,t;
    tree[x][1]=0;
    father[z]=0;
    pp[z]=x;
    t=-inf;
    fo(i,0,1)
        fo(j,0,1)
            t=max(t,f[z][i][j]);
    g[x][0]+=t;
    t=-inf;
    fo(i,0,1) t=max(t,f[z][0][i]);
    g[x][1]+=t;
    update(x);
}
void empty_real(int x,int z){
    int i,j,t;
    tree[x][1]=z;
    father[z]=x;
    pp[z]=0;
    t=-inf;
    fo(i,0,1)
        fo(j,0,1)
            t=max(t,f[z][i][j]);
    g[x][0]-=t;
    t=-inf;
    fo(i,0,1) t=max(t,f[z][0][i]);
    g[x][1]-=t;
    update(x);
}
void access(int x){
    int y,z;
    splay(x,0);
    if (tree[x][1]){
        z=tree[x][1];
        real_empty(x,z);
    }
    while (pp[x]){
        y=pp[x];
        splay(y,0);
        if (tree[y][1]){
            z=tree[y][1];
            real_empty(y,z);
        }
        empty_real(y,x);
        splay(x,0);
    }
}
void link(int x,int y){
    access(x);
    splay(x,0);
    g[x][0]+=1;
    pp[y]=x;
    update(x);
}
int main(){
    freopen("party.in","r",stdin);freopen("party.out","w",stdout);
    n=read();czy=read();
    n++;
    tot=1;
    update(tot);
    fo(i,1,n-1){
        j=read();
        if (czy) j^=ans;
        j++;
        ++tot;
        update(tot);
        link(j,tot);
        access(1);
        ans=max(f[1][0][0],f[1][1][1]);
        printf("%d\n",ans);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值