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

原题链接: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_back
using 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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值