codeforces #343d water tree(dfs+线段树)

来源:互联网 发布:黑客获取数据 编辑:程序博客网 时间:2024/05/17 03:07

原题链接:http://codeforces.com/contest/343/problem/D
标签:线段树,dfs
大致题意:(搬运自dm的ppt)
这里写图片描述
这道题的解法如果没看过题解,是比较难想到的。这道题的解法就是读入完所有的树边后,用dfs搜一遍,得到每个节点的的儿子所在的区间,用in和out来表示。然后这道题就可以转化为线段树的问题。
那么如何实现三种操作呢?我们可以对于每个节点维护sum和f两个值。我们设用1表示该节点有水,0表示该节点没水,那么sum表示该点及其所有儿子的值的总和。用f来维护该点及其所有儿子是否都有(没)水。
对于操作1,如果该点存在值为0的儿子,那么把该点的父亲赋为0(想一想,为什么),然后进行一次区间修改即可。
对于操作2,直接将该点标记为空。
对于查询,将该点用query去查询已该点为根,有多少叶子节点的值为1,将该值与它的叶子节点数进行比较,即可得出子树内是否有节点值为0。如有,输出0;否则输出1。
这道题建议大家在调试时使用静态调试,不然需要调试很久(我自己就是这样)。
附上代码

#include<cstdio>#include<algorithm>#include<cstring>#include<vector>#define maxn 500050#define pb push_backusing namespace std;struct node{    int l,r,f,sum; };int n,i,u,v,top,q; int in[maxn+5],out[maxn+5],fa[maxn+5],dfn,vis[maxn+5]; //fa数组记录每个点的父亲 vector<int> t[maxn]; int ci,vi;node tree[maxn*4+5];void dfs(int x,int fat){    in[x]=++dfn; fa[x]=fat; vis[x]=1;    for(int h=0;h<t[x].size();h++)         if (vis[t[x][h]]==0) dfs(t[x][h],x);    out[x]=dfn;}void pushdown(int x) //利用f拆分计算sum {    if (tree[x].f!=-1) //如果l~r全部已有水     {        tree[x*2].f=tree[x*2+1].f=tree[x].f;        tree[x*2].sum=(tree[x*2].r-tree[x*2].l+1)*tree[x].f;        tree[x*2+1].sum=(tree[x*2+1].r-tree[x*2+1].l+1)*tree[x].f;        tree[x].f=-1;    } }  void build(int x,int l,int r){    tree[x].l=l; tree[x].r=r; tree[x].f=-1; tree[x].sum=0;    if (l!=r)     {        int mid=(l+r)/2; build(x*2,l,mid); build(x*2+1,mid+1,r);    }}void update(int x,int l,int r,int val) //区间修改 {    if (l<=tree[x].l&&tree[x].r<=r)    {        tree[x].f=val; tree[x].sum=val*(tree[x].r-tree[x].l+1);    }    else    {        pushdown(x); int mid=(tree[x].l+tree[x].r)/2;        if (l<=mid) update(x*2,l,r,val);        if (r>mid) update(x*2+1,l,r,val);        tree[x].sum=tree[x*2].sum+tree[x*2+1].sum;    }}int query(int x,int l,int r){    int maxl,minr;    if (tree[x].f!=-1)    {        maxl=max(l,tree[x].l); minr=min(r,tree[x].r);        return tree[x].f*(minr-maxl+1);    }    if (l<=tree[x].l&&tree[x].r<=r) return tree[x].sum;    else     {        int num=0; int mid=(tree[x].l+tree[x].r)/2;        if (l<=mid) num+=query(x*2,l,r);        if (r>mid) num+=query(x*2+1,l,r);            return num;    }}int main(){    scanf("%d",&n); memset(fa,0,sizeof(fa)); memset(vis,0,sizeof(vis));     for (i=1;i<n;i++) scanf("%d%d",&u,&v),t[u].pb(v),t[v].pb(u);    dfn=0; dfs(1,0); build(1,1,dfn);    scanf("%d",&q);    while (q--)    {        scanf("%d%d",&ci,&vi);        if (ci==1)        {            if (query(1,in[vi],out[vi])<(out[vi]-in[vi]+1)&&fa[vi]!=0) //判断是否有儿子为空,并且它本身不是根节点                 update(1,in[fa[vi]],in[fa[vi]],0); //将其父亲标记为空            update(1,in[vi],out[vi],1); //将其所有儿子标记为有水         }        if (ci==2)            update(1,in[vi],in[vi],0); //将该点标记为空        if (ci==3)            if (query(1,in[vi],out[vi])<(out[vi]-in[vi]+1)) printf("0\n");            else printf("1\n");     }    return 0;}
0 0
原创粉丝点击