codeforces 1017G. The Tree

题意:

给一颗一开始全是白点的树,支持三个操作:
1:将某一个点改成白点,假如已经是白点,则对儿子进行该操作。
2:将一颗子树改成白色
3:询问某个点的颜色

题解:

显然是在要操作的点上打上标记。
关键是要设计一种有用的标记,比赛时sb,没有想到可以用后缀和……
一开始整棵树都是-1,对于一操作,就在该点上+1,那么一个点上是黑点当前仅当它到根的路径的最大后缀和>=0。
2操作等于将一棵子树标为-1,但是这样也不能保证子树中的点的最大后缀和=-1,所以要在该子树的根将子树外的影响消掉。
code:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
const int inf=1<<28;
struct node{int c,max;};
struct trnode{
    int fa,dep,tot,top,son,lc,rc;
    bool u;node c;
    trnode() {u=false;}
}tr[200010];int tot=0;
int ys[100010],la[100010],z=0;
struct Node{
    int y,next;
}a[200010];int len=0,last[100010];
int n,q;
void ins(int x,int y)
{
    a[++len].y=y;
    a[len].next=last[x];last[x]=len;
}
void pre_node(int x,int fa)
{
    tr[x].fa=fa;tr[x].dep=tr[fa].dep+1;tr[x].tot=1;
    for(int i=last[x];i;i=a[i].next)
    {
        int y=a[i].y;
        if(y==fa) continue;
        pre_node(y,x);tr[x].tot+=tr[y].tot;
        if(tr[tr[x].son].tot<tr[y].tot) tr[x].son=y;
    }
}
void pre_edge(int x,int top)
{
    tr[x].top=top;ys[x]=la[x]=++z;
    if(tr[x].son) pre_edge(tr[x].son,top),la[x]=la[tr[x].son];
    for(int i=last[x];i;i=a[i].next)
    {
        int y=a[i].y;
        if(y==tr[x].fa||y==tr[x].son) continue;
        pre_edge(y,y);la[x]=la[y];
    }
}
node update(node lc,node rc)
{
    node ans;ans.c=lc.c+rc.c;
    ans.max=max(rc.max,lc.max+rc.c);
    return ans;
}
int bt(int l,int r)
{
    int x=++tot;
    if(l!=r)
    {
        int mid=(l+r)/2;
        tr[x].lc=bt(l,mid);
        tr[x].rc=bt(mid+1,r);
        tr[x].c=update(tr[tr[x].lc].c,tr[tr[x].rc].c);
    }
    else tr[x].c.c=tr[x].c.max=-1;
    return x;
}
void pushdown(int x,int l,int r)
{
    int lc=tr[x].lc,rc=tr[x].rc,mid=(l+r)/2;
    tr[lc].c.c=-(mid-l+1);tr[lc].c.max=-1;tr[lc].u=true;
    tr[rc].c.c=-(r-mid);tr[rc].c.max=-1;tr[rc].u=true;
    tr[x].u=false;
}
void change(int x,int l,int r,int fl,int fr,int c)
{
    if(l==fl&&r==fr)
    {
        if(c==inf) tr[x].c.c=-(r-l+1),tr[x].c.max=-1,tr[x].u=true;
        else tr[x].c.c+=c,tr[x].c.max+=c;
        return;
    }
    if(tr[x].u) pushdown(x,l,r);
    int mid=(l+r)/2;
    if(fr<=mid) change(tr[x].lc,l,mid,fl,fr,c);
    else if(fl>mid) change(tr[x].rc,mid+1,r,fl,fr,c);
    else change(tr[x].lc,l,mid,fl,mid,c),change(tr[x].rc,mid+1,r,mid+1,fr,c);
    tr[x].c=update(tr[tr[x].lc].c,tr[tr[x].rc].c);
}
node findans(int x,int l,int r,int fl,int fr)
{
    if(l==fl&&r==fr) return tr[x].c;
    if(tr[x].u) pushdown(x,l,r);
    int mid=(l+r)/2;
    if(fr<=mid) return findans(tr[x].lc,l,mid,fl,fr);
    if(fl>mid) return findans(tr[x].rc,mid+1,r,fl,fr);
    return update(findans(tr[x].lc,l,mid,fl,mid),findans(tr[x].rc,mid+1,r,mid+1,fr));
}
int solve(int x)
{
    node ans;ans.c=0;ans.max=-inf;
    int tx=tr[x].top;
    while(tx!=1)
    {
        ans=update(findans(1,1,n,ys[tx],ys[x]),ans);
        x=tr[tx].fa;tx=tr[x].top;
    }
    node k=findans(1,1,n,ys[1],ys[x]);
    //printf("solve:%d %d %d %d\n",tx,x,k.c,k.max);
    ans=update(k,ans);
    //printf("%d %d\n",ans.c,ans.max);
    return ans.max;
}
int main()
{
    scanf("%d %d",&n,&q);
    bt(1,n);
    for(int i=2;i<=n;i++)
    {
        int x;scanf("%d",&x);
        ins(x,i);ins(i,x);
    }
    pre_node(1,0);pre_edge(1,1);
    //for(int i=1;i<=n;i++) printf("%d %d\n",ys[i],la[i]);
    while(q--)
    {
        int op,x;scanf("%d %d",&op,&x);
        if(op==1) change(1,1,n,ys[x],ys[x],1);
        if(op==2)
        {
            change(1,1,n,ys[x],la[x],inf);
            int k=solve(x);
            if(k>-1) change(1,1,n,ys[x],ys[x],-(k+1));
        }
        if(op==3)
        {
            int k=solve(x);
            if(k>-1) printf("black\n");
            else printf("white\n");
        }
        //for(int i=1;i<=n;i++) printf("%d ",findans(1,1,n,ys[i],ys[i]).max);printf("\n");
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值