Codeforces 343D Water Tree-线段树

传送门
题意:
给你一棵树,有三种操作:
1. 给一个点及其子孙赋值为1
2. 给一个点及其祖先赋值为0
3. 求一个点是1还是0

Solution:
这道题显然可以用树剖做,但是复杂度不优秀(两个log),这里说一种一个log的做法:
先跑出树的dfs序,对dfs序建一棵线段树,那么操作1就变的非常简单,我们重点需要考虑的是操作2:暴力给每个点赋值是会T的,我们可以先只修改一个最下面的点,即这个点本身,那么就需要把3操作改为查询这个点的子树是否全1,因为如果这个点的子树中不是全1,那么这个点一定是0,只是这个点并没有被更新到而已,同时,在操作1中,如果我们选中的这个点为0,那么我们就需要把0往上更新,因为这个点及其子树将会再次被全赋成1,如果不往上更新的话0的信息就会被覆盖从而导致体现不出来了。

代码:

#include<cstdio>
#include<iostream>
using namespace std;
const int N=500010;
struct edg{
    int to,next;
}e[2*N];
int n,m;
int fa[N];
int size,head[N];
bool vis[N];
struct tree{
    int l,r;
    bool v,tag;
}tr[4*N];
int x,y,tot;
int bg[N],ed[N];
void add(int x,int y)
{
    size++;
    e[size].to=y;
    e[size].next=head[x];
    head[x]=size;
}
void dfs(int x,int f)
{
    bg[x]=++tot;
    fa[x]=f;
    for (int i=head[x];i;i=e[i].next)
    {
        int y=e[i].to;
        if (y!=f) dfs(y,x);
    }
    ed[x]=tot;
}
void update(int i)
{
    tr[i].v=tr[i<<1].v*tr[i<<1|1].v;
}
void build(int i,int l,int r)
{
    tr[i].l=l;tr[i].r=r;
    if (l==r) return;
    int mid=l+r>>1;
    build(i<<1,l,mid);
    build(i<<1|1,mid+1,r);
}
void addtag(int x,int val)
{
    tr[x].tag=val;
    tr[x].v=val;
}
void pushdown(int i)
{
    if (tr[i].tag)
    {
        tr[i].tag=0;
        if (tr[i].l==tr[i].r) return;
        addtag(i<<1,1);
        addtag(i<<1|1,1);
    }
}
void modify(int i,int l,int r)
{
    int L=tr[i].l,R=tr[i].r;
    if (l>R||L>r) return;
    pushdown(i);
    if (l<=L&&R<=r){addtag(i,1);return;}
    modify(i<<1,l,r);
    modify(i<<1|1,l,r);
    update(i);
}
void md0(int i,int pos)
{
    int L=tr[i].l,R=tr[i].r;
    pushdown(i);
    if (L==R) {tr[i].v=0;return;}
    int mid=L+R>>1;
    if (pos<=mid) md0(i<<1,pos);
    else md0(i<<1|1,pos);
    update(i);
}
bool query(int i,int l,int r)
{
    int L=tr[i].l,R=tr[i].r;
    if (l>R||L>r) return 1;
    pushdown(i);
    if (l<=L&&R<=r){return tr[i].v;}
    return query(i<<1,l,r)*query(i<<1|1,l,r);
}
int main()
{
    //freopen("343D.in","r",stdin);
    //freopen("343D.out","w",stdout);
    scanf("%d",&n);
    for (int i=1;i<n;i++)
        scanf("%d%d",&x,&y),add(x,y),add(y,x);
    dfs(1,0);
    build(1,1,tot);
    scanf("%d",&m);
    for (int i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        if (x==1) {if (y!=1&&!query(1,bg[y],ed[y]))md0(1,bg[fa[y]]);modify(1,bg[y],ed[y]);}
        else if (x==2) md0(1,bg[y]);
        else if (x==3) printf("%d\n",query(1,bg[y],ed[y]));
    }
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值