codeforce-343D:Water Tree(线段树+脑洞)


题目链接:http://codeforces.com/problemset/problem/343/D


题目大意:

有n个水库,编号1到n,它们之间互相连接形成一颗树,然后对水库进行以下操作,为了方便,接下来用结点说明。

操作1,将某个结点及它的儿子结点赋为1.

操作2,将某个结点及它的父亲结点赋为0.

操作3,查询某个结点当前的值.


解题思路:

因为之前做过一道类似的题目,很快就明白首先要将树的结构转换为线性的结构。具体的操作就是将树遍历一遍然后找出每个结点对应的所管辖的区间。这个操作也比较容易实现,但是这个题目有一点卡了很久,就是将某个结点以及它的父亲结点赋为0,当时想用同样的方法用区间表示,想了一个多小时都没想出来该怎么处理。想了几种方法均有漏洞。实在无果,去网上喵了一眼正确解法,发现了这题的奥妙所在。


因为如果将某个结点置为0,那么它的父亲结点应该都是0.这样的话我们更新的时候就可以先只更新这个结点值为0.然后查询某个结点值的时候,直接查询它所管辖区间即它的所有儿子中是否含有为0的结点,如果有就,该节点值即为0.


另外一个比较重要的操作就是操作1.当我们执行操作1的时候,要首先查询它的儿子结点中是否有0结点。如果有的话,就先将当前要更新结点的父亲结点(这个需要专门记录一下)的值更新为0,这是很关键的一步。保证了这个思路的正确性。

之后就是线段树的正常更新了,以下贴代码,注意因为codeforce是单组测试所以没有进行vis数组和vector的初始化,


#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
#include<set>
#include<cmath>
#include<vector>
#define pb push_back
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
typedef long long ll;
const int N=500010;
int n,m,idx,fl;
struct node
{
    int lv,rv;  //记录它当前管辖区间的左右端点
    int fa;     //记录它的父亲结点
}a[N];
bool vis[N];    
vector<int> v[N];   //建图
struct tree
{
    int l,r,mid;
    int lazy;
    int cnt;
}t[N<<2];
void build(int l,int r,int rt)  //建树
{
    int m=(l+r)>>1;
    t[rt].l=l;t[rt].r=r;
    t[rt].mid=m;
    t[rt].cnt=0;
    t[rt].lazy=0;
    if(l==r)
        return ;
    build(l,m,lson);
    build(m+1,r,rson);
}
void pushup(int rt) //如果子节点有0 则为0
{
    if(t[lson].cnt==0||t[rson].cnt==0)
        t[rt].cnt=0;
    else
        t[rt].cnt=1;
}
void pushdown(int rt)   //延迟更新
{
    if(t[rt].lazy)
    {
        t[lson].cnt=t[rson].cnt=t[rt].cnt;
        t[lson].lazy=t[rson].lazy=1;
        t[rt].lazy=0;
    }
}
void update(int p,int q,int flag,int rt)    //正常的线段树更新
{
    if(p<=t[rt].l&&t[rt].r<=q)
    {
        t[rt].cnt=flag;
        t[rt].lazy=1;
        return ;
    }
    pushdown(rt);
    if(p<=t[rt].mid)
        update(p,q,flag,lson);
    if(q>t[rt].mid)
        update(p,q,flag,rson);
    pushup(rt);
}
void query(int p,int q,int rt)  //正常的线段树查询
{
    if(fl)
        return ;
    if(t[rt].l>=p&&t[rt].r<=q)
    {
        if(t[rt].cnt==0)
            fl=1;
        return ;
    }
    pushdown(rt);
    if(p<=t[rt].mid)
        query(p,q,lson);
    if(q>t[rt].mid)
        query(p,q,rson);
    pushup(rt);
}
void dfs(int k,int fa)  //遍历树 将树转换为线性结构
{
    a[k].lv=idx++;  //记录左端点
    vis[k]=1;       
    a[k].fa=fa;     //记录父亲结点
    for(int i=0;i<(int)v[k].size();i++)
    {
        if(!vis[v[k][i]])
            dfs(v[k][i],k);
    }
    a[k].rv=idx-1;  //记录右端点
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        int p,q;
        for(int i=1;i<=n-1;i++)
        {
            scanf("%d%d",&p,&q);
            v[p].pb(q); //建图
            v[q].pb(p);
        }
        build(1,n,1);
        idx=1;
        dfs(1,0);   //遍历
        scanf("%d",&m);
        while(m--)  //跟解题思路里面说的步骤一样
        {
            scanf("%d%d",&p,&q);
            if(p==1)
            {
                fl=0;
                query(a[q].lv,a[q].rv,1);
                if(fl)
                    update(a[a[q].fa].lv,a[a[q].fa].lv,0,1);
                update(a[q].lv,a[q].rv,1,1);
            }
            if(p==2)
                update(a[q].lv,a[q].lv,0,1);
            if(p==3)
            {
                fl=0;
                query(a[q].lv,a[q].rv,1);
                if(fl)
                    printf("0\n");
                else
                    printf("1\n");
            }
        }
    }
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值