p4332(三叉神经树 lct维护链上信息 好题)

题目
在这里插入图片描述
在这里插入图片描述
感谢洛谷题解的第二篇!!!
在这里插入图片描述在这里插入图片描述在这里插入图片描述我也不知道说什么。。对lct理解的不够好。。。

//此题没有makeroot lct中的深度大小对比就是原树中的
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=(5e5+5)*3;
int f[N],ch[N][2],st[N],tag[N],A[N],B[N],oneson[N],col[N];
struct Edge{int to,nex;}edge[N];
int head[N],tot;
void add(int from,int to){
    edge[++tot]=(Edge){to,head[from]};head[from]=tot;
}
void dfs(int x){
    for(int i=head[x];i;i=edge[i].nex){
        int y=edge[i].to;
        dfs(y);oneson[x]+=(col[y]==1);
    }
    if(oneson[x]>=2) col[x]=1;
}             //因为拉这个链的时候 这个连只是路径上的点 所以二者是一个意思
void modify(int x,int o){//至于为什么我一直用不为1最深的点 而不是从k向上连续到哪里不是1这样表述
    oneson[x]+=o,tag[x]+=o;//是为了这样的话pushup pushdown modify容易理解
    swap(A[x],B[x]);//A[X]:指x子树中最大深度不为1的点 所以A[X],B[x]一定有一个为x这颗子树深度最大的点 modify之后相当于互换
}
//将k节点0变为1(颜色):if(oneson[f[k]]) col[f[k]]颜色由0变为1 同理f[k]可能会影响f[f[k]].所以我们要从k出发向上直到root(1)的最长一段连续的1
void pushup(int x){//我们想拉的链为k-root(1).  A[x]:此时拉链过程或者splay过程x这颗子树不为1深度最大的点 B[X]:.....不为2深度最大的点
    int ls=ch[x][0],rs=ch[x][1];
    A[x]=A[ls],B[x]=B[ls];
    if(oneson[x]!=1) A[x]=x;//假如x的子树中比x深度小的儿子不存在(会有没有1)的点
    if(oneson[x]!=2) B[x]=x;
    if(A[rs]) A[x]=A[rs];//假如x的右子树存在不为1的点(深度肯定大于x)
    if(B[rs]) B[x]=B[rs];
}
void pushdown(int x){
    if(!tag[x]) return;//emm 我一开始以为下面modify也是大大的错误 因为我认为出发点k会变
    modify(ch[x][0],tag[x]),modify(ch[x][1],tag[x]);//但我们要时刻记住A[x] B[x] 只是当前x所在splay子树中第一个不等于0
    tag[x]=0;//不要下意识的带有k怎么样 即使k发生了变化 这个当前splay的子树也没必要拆散完啊(要理解lct的本质)
}
inline int notroot(int x){//不是当前所在splay的根
    return ch[f[x]][0]==x||ch[f[x]][1]==x;
}
inline void route(int x){
    int y=f[x],z=f[y],w=(ch[y][1]==x);
    if(notroot(y)) ch[z][ch[z][1]==y]=x;//先这句话!!!
    f[x]=z;
    ch[y][w]=ch[x][w^1];if(ch[y][w]) f[ch[y][w]]=y;
    ch[x][w^1]=y,f[y]=x;
    pushup(y),pushup(x);
}
inline void splay(int x){
    int y=x,num=0;st[++num]=y;
    while(notroot(y)) st[++num]=(y=f[y]);
    while(num) pushdown(st[num--]);
    if(!notroot(x)) return;
    for(int fa=f[x];notroot(x);route(x),fa=f[x])
        if(notroot(fa)) ((ch[fa][1]==x)^(ch[f[fa]][1]==fa))?route(x):route(fa);
}
void access(int x){
    for(int y=0;x;x=f[y=x])
        splay(x),ch[x][1]=y,pushup(x);
}
int solve(int x){
    int o=col[x],fa;
    access(fa=f[x]),splay(fa);
    int dex=(!o)?A[fa]:B[fa];o=(!o)?1:-1;
    if(!dex){//k-root(1)里面所有的值都需要改变.
        modify(fa,o);
        return col[1]^=1;
    }
    splay(dex);//此时dex就是当前splay的顶点 当前splay里面的所有点都是k-root(1)路径里面的点.
    modify(ch[dex][1],o),oneson[dex]+=o;//不要忘记oneson[dex]!!!因为dex可没有lazy标记 要处理好
    return col[1];//one[dex]的增加是因为有且只有一个儿子颜色改变了!(因为一条链中存在兄弟节点 只能父与子) 这只是单纯的原树中的三个儿子 和lct一点关系都没
}
int main()
{
    int n;scanf("%d",&n);
    for(int i=1;i<=n;++i)
    {
        int x,y,z;scanf("%d%d%d",&x,&y,&z);
        f[x]=f[y]=f[z]=i;
        add(i,x),add(i,y),add(i,z);
    }
    for(int i=n+1;i<=3*n+1;++i) scanf("%d",&col[i]);
    dfs(1);
    int m;scanf("%d",&m);
    while(m--)
    {
        int k;scanf("%d",&k);//将x这个节点的颜色改变
        printf("%d\n",solve(k));col[k]^=1;//一开始觉着col[k]更新无所谓的 只要col[1]更新就行了 但是solve时需要这个信息啊啊...
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值