bzoj3553

题意:
有一棵三叉树,n个非叶节点。叶节点有0或1,非叶节点的值为3个孩子中较多的值。q个操作修改某个叶节点的值,问根的值。
n≤500000,q≤500000

#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<iostream>
#define N 510000
using namespace std;
struct node{int s1,s2,s3,tp,fa;}t[3*N];
struct node1{int l,r,lc,rc,rev,p;}lt[2*N];
int s[3*N],siz[N],n,m,dfn[N],id,w[N],tl;
bool b[N];
void dfs(int x)
{
    siz[x]=1;
    if(t[x].s1<=n)
    {
        dfs(t[x].s1);
        siz[x]+=siz[t[x].s1];
    }
    if(t[x].s2<=n)
    {
        dfs(t[x].s2);
        siz[x]+=siz[t[x].s2];
        if(siz[t[x].s2]>siz[t[x].s1]) swap(t[x].s1,t[x].s2);
    }
    if(t[x].s3<=n)
    {
        dfs(t[x].s3);
        siz[x]+=siz[t[x].s3];
        if(siz[t[x].s3]>siz[t[x].s1]) swap(t[x].s1,t[x].s3);
    }
}
void dfs1(int x,int fa,int tp)
{
    dfn[x]=++id;w[dfn[x]]=x;t[x].tp=tp;
    if(t[x].s1<=n) dfs1(t[x].s1,x,tp);
    if(t[x].s2<=n) dfs1(t[x].s2,x,t[x].s2);
    if(t[x].s3<=n) dfs1(t[x].s3,x,t[x].s3);
}
void pre_s(int x)
{
    if(t[x].s1<=n) pre_s(t[x].s1);
    if(t[x].s2<=n) pre_s(t[x].s2);
    if(t[x].s3<=n) pre_s(t[x].s3);
    int cnt[2];cnt[0]=cnt[1]=0;
    cnt[s[t[x].s1]]++;cnt[s[t[x].s2]]++;cnt[s[t[x].s3]]++;
    if(cnt[0]>cnt[1]) s[x]=0;
    else s[x]=1;
    if(cnt[s[t[x].s1]]==2) b[x]=1;
}
void upd(int now)
{
    int lc=lt[now].lc,rc=lt[now].rc,mid=(lt[now].l+lt[now].r)/2;
    if(lt[rc].p==-1) {lt[now].p=-1;return;}
    if(lt[lc].p==-1 || lt[rc].p!=mid+1) lt[now].p=lt[rc].p;
    else lt[now].p=lt[lc].p;
}
void bt(int l,int r)
{
    int now=++tl;
    lt[now].l=l;lt[now].r=r;
    if(l<r)
    {
        int mid=(l+r)/2;
        lt[now].lc=tl+1;bt(l,mid);
        lt[now].rc=tl+1;bt(mid+1,r);
        upd(now);
    }
    else
    {
        if(b[w[l]]) lt[now].p=l;
        else lt[now].p=-1;
    }
}
int find(int now,int k)
{
    int lc=lt[now].lc,rc=lt[now].rc,mid=(lt[now].l+lt[now].r)/2;
    if(lt[now].l==lt[now].r) return lt[now].rev;
    if(mid>=k) return find(lc,k)^lt[now].rev;
    else return find(rc,k)^lt[now].rev;
}
void get_s(int x,int *c)
{
    int o;c[0]=c[1]=0;
    o=s[t[x].s1];
    if(t[x].s1<=n) o^=find(1,dfn[t[x].s1]);
    c[o]++;
    o=s[t[x].s2];
    if(t[x].s2<=n) o^=find(1,dfn[t[x].s2]);
    c[o]++;
    o=s[t[x].s3];
    if(t[x].s3<=n) o^=find(1,dfn[t[x].s3]);
    c[o]++;
}
int query(int now,int l,int r)
{
    int lc=lt[now].lc,rc=lt[now].rc,mid=(lt[now].l+lt[now].r)/2;
    if(lt[now].l==l && lt[now].r==r) return lt[now].p;
    if(mid>=r) return query(lc,l,r);
    else if(l>mid) return query(rc,l,r);
    else
    {
        int p1,p2=query(rc,mid+1,r);
        if(p2!=mid+1) return p2;
        p1=query(lc,l,mid);
        if(p1==-1) return p2;
        return p1;
    }
}
void reverse(int now,int l,int r)
{
    int lc=lt[now].lc,rc=lt[now].rc,mid=(lt[now].l+lt[now].r)/2;
    if(lt[now].l==l && lt[now].r==r) {lt[now].rev^=1;return;}
    if(mid>=r) reverse(lc,l,r);
    else if(l>mid) reverse(rc,l,r);
    else reverse(lc,l,mid),reverse(rc,mid+1,r);
}
void change(int now,int k,int o)
{
    int lc=lt[now].lc,rc=lt[now].rc,mid=(lt[now].l+lt[now].r)/2;
    if(lt[now].l==lt[now].r) {if(o) lt[now].p=k;else lt[now].p=-1;return;}
    if(mid>=k) change(lc,k,o);
    else change(rc,k,o);
    upd(now);
}
void cal(int x,int *c)
{
    int o=s[t[x].s1];
    if(t[x].s1<=n) o^=find(1,dfn[t[x].s1]);
    if(c[o]==2) change(1,dfn[x],1);
    else change(1,dfn[x],0);
}
void modify(int x)
{
    int fx=t[x].tp,c[2];
    s[x]^=1;
    while(x)
    {
        if(x!=fx)
        {
            int p=query(1,dfn[fx],dfn[x]-1);
            if(p==-1) break;
            reverse(1,p,dfn[x]-1);
            if(w[p]!=fx) break;
        }
        x=t[fx].fa;fx=t[x].tp;
        if(x==0) break;
        get_s(x,c);cal(x,c);
        int o=s[x]^find(1,dfn[x]);
        if(c[o]>c[o^1]) break;
        s[x]^=1;
    }
}
void solve()
{
    scanf("%d",&m);
    while(m--)
    {
        int x,c[2];scanf("%d",&x);
        get_s(t[x].fa,c);
        if(c[s[x]]==2) modify(t[x].fa);
        s[x]^=1;
        get_s(t[x].fa,c);cal(t[x].fa,c);
        printf("%d\n",s[1]^find(1,1));
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d%d",&t[i].s1,&t[i].s2,&t[i].s3);
        t[t[i].s1].fa=t[t[i].s2].fa=t[t[i].s3].fa=i;
    }
    dfs(1);
    dfs1(1,0,1);
    for(int i=n+1;i<=3*n+1;i++) scanf("%d",&s[i]);
    pre_s(1);
    bt(1,n);
    solve();
    return 0;
}


题解:
感觉这是属于那一类很妙的树剖的(套路)。。
易知某个点x修改颜色影响fa的条件是fa的3个孩子中有2个是x的颜色,而且注意如果x修改前会影响fa,那修改后依然会影响fa。因为x颜色的总是有2个。同理如果修改前影响不到,那修改后也影响不到。
每次修改影响的都是自底向上连续的一段,那就可以考虑用树剖。x维护的信息是x的重孩子的修改会不会影响x。
对于一段重链,我们确定底端的点要修改了,然后就自底向上找一段最长的会被重孩子影响的,给这一段颜色取反。
如果没找到链顶,退出即可,上面说过了这样的修改不会影响任何人线段树上的值。
如果找到了链顶,就跳到上一条重链的链底,暴力找他3个孩子看他此时受不受影响,同时修改他线段树上的值。如果受影响,重复上面过程。
3叉树的意义在于跳重链时只用暴力查3个值,不破坏复杂度。
看别人题解线段树似乎是维护区间内孩子三元组是(0,0,1),(0,1,1)的信息,取反相当于(0,0,1)变(0,1,1),(0,1,1)变(0,0,1)。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值