文章标题 Coderforces 343D : Water Tree(dfs序+线段树)

Water Tree

题意:有n个点的树,1为根,然后有三种操作
(1)将节点u及其子树的所有节点变为1
(2)将节点u变为0
(3)查询节点u及其子树的所有节点是否为1
分析:首先可以将树形结构变为线性结构,即用DFS序将节点u及其控制的子树节点变成区间[ in[u] , out[u] ],然后对于(2)操作,就是线段树的单点修改;对于(1)操作,由于当节点u原来为0,其所有的祖先节点都为0,那么我们可以先将其父节点单点修改为0(相当于将其所有祖先节点改为0),然后再将u节点及其子树节点改为1,这个操作就是区间更新;对于(3)操作,我们其实就是查询节点u所控制的区间是否全部为1,那么就是区间查。这个过程用&运算来来保存节点的信息。
代码:

#include<iostream>
#include<string>
#include<cstdio>
#include<cstring>
#include<vector>
#include<math.h>
#include<map>
#include<queue> 
#include<algorithm>
using namespace std;
const int inf = 0x3f3f3f3f;
typedef pair<int,int> pii;

const int maxn = 5e5 + 10;

int n;

int dfn[maxn],in[maxn],out[maxn];
int fa[maxn];
int idx;
struct Edge{
    int to,nex;
}edge[maxn*2];
int tot,head[maxn];
void addedge(int u,int v){
    edge[tot]=Edge{v,head[u]};
    head[u]=tot++;
}
void init(){
    tot=idx=0;
    memset (head,-1,sizeof (head));
}

void dfs(int u,int pre){//求dfs序和fa数组 
    dfn[u]=++idx;
    fa[u]=pre;
    in[u]=idx;
    for (int i=head[u];i!=-1;i=edge[i].nex){
        int v=edge[i].to;
        if (v==pre)continue;
        dfs(v,u);
    }
    out[u]=idx;
}

int tree[maxn*4];
int setv[maxn*4];//lazy标记
void push_down(int rt){
    if (setv[rt]==1){
        setv[rt*2]=setv[rt*2+1]=setv[rt];
        tree[rt*2]=tree[rt*2+1]=setv[rt];
        setv[rt]=-1;
    }
} 
void update (int rt,int l,int r,int L,int R,int val){//区间更新 
    if (l>=L&&r<=R){
        setv[rt]=val;
        tree[rt]=val;
        return ;
    }
    push_down(rt);
    int mid=(l+r)/2;
    if (L<=mid)update(rt*2,l,mid,L,R,val);
    if (R>mid)update(rt*2+1,mid+1,r,L,R,val);
    tree[rt]=tree[rt*2]&&tree[rt*2+1];
}

void update (int rt,int l,int r,int pos){//单点修改 
    if (l==r){
        tree[rt]=0;
        return;
    }
    push_down(rt);
    int mid=(l+r)/2;
    if (pos<=mid)update(rt*2,l,mid,pos);
    else update(rt*2+1,mid+1,r,pos);
    tree[rt]=tree[rt*2]&&tree[rt*2+1];
}
int query(int rt,int l,int r,int L,int R){//区间查询 
    if (l>=L&&R>=r){
        return tree[rt];
    }
    push_down(rt);
    int mid=(l+r)/2;
    int ans=1000000;
    if (L<=mid){
        ans = min(query(rt*2,l,mid,L,R),ans);
    }
    if (R>mid)ans = min(query(rt*2+1,mid+1,r,L,R),ans);
    return ans;
}

int main ()
{

    while(scanf ("%d",&n)!=EOF){
        init();
        memset (tree,0,sizeof (tree));
        memset (setv,-1,sizeof (setv)); 
        int u,v;
        for (int i=0;i<n-1;i++){
            scanf ("%d%d",&u,&v);
            addedge(u,v);
            addedge(v,u);
        }
        dfs(1,-1);
        int q,op;
        scanf ("%d",&q);
        while (q--){
            scanf ("%d%d",&op,&u);
            if (op==1){
                if (!query(1,1,idx,in[u],out[u])&&fa[u]!=-1){//如果该节点的父节点为为0,则修改 
                    update(1,1,idx,in[fa[u]]);
                }
                update(1,1,idx,in[u],out[u],1);
            }else if (op==2){
                update(1,1,idx,in[u]);
            }else {
                int ans = query(1,1,idx,in[u],out[u]);
                printf ("%d\n",ans);
            }
        }
    }   
    return 0;
}
/*
5
1 2
5 1
2 3
4 2
12
1 1
2 3
3 1
3 2
3 3
3 4
1 2
2 4
3 1
3 3
3 4
3 5
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值