Description

Translated by @Nishikino_Maki from Luogu
行吧是我翻的

  • Mad scientist Mike has constructed a rooted tree, which consists of n n vertices. Each vertex is a reservoir which can be either empty or filled with water.
  • The vertices of the tree are numbered from 1 to n n with the root at vertex 1. For each vertex, the reservoirs of its children are located below the reservoir of this vertex, and the vertex is connected with each of the children by a pipe through which water can flow downwards.
  • Mike wants to do the following operations with the tree:
  1. Fill vertex v with water. Then v and all its children are filled with water.
  2. Empty vertex v . Then v and all its ancestors are emptied.
  3. Determine whether vertex v is filled with water at the moment.
  • Initially all vertices of the tree are empty.Mike has already compiled a full list of operations that he wants to perform in order. Before experimenting with the tree Mike decided to run the list through a simulation. Help Mike determine what results will he get after performing all the operations.

  • 疯狂科学家Mike培养了一颗有根树,由n个节点组成。每个节点是一个要么装满水要么为空的贮水容器. 树的节点用1~n编号,其中根节点为1.对于每个节点的容器,其子节点的容器均在这一容器下方,并且每个节点都由一根可以向下流水的管道与其子节点连接. Mike想要对这棵树做以下操作:

  1. 将节点v注满水. 这样v和其子节点都会充满水.
  2. 将节点v置空. 这样v及其祖先节点(从v到根节点的路径)都会被置空.
  3. 查询当前节点v是否充满水.
  • 初始时,所有节点都为空. Mike已经制定好了他的操作顺序. 在对树进行实验前,他决定先模拟一下操作. 请你帮助Mike得出他操作后的结果.

Input&Output

Input

  • The first line of the input contains an integer n ( 1<=n<=500000 ) — the number of vertices in the tree. Each of the following n−1 lines contains two space-separated numbers ai, bi(1<=ai,bi<=n, ai≠bi) — the edges of the tree.
  • The next line contains a number q (1 ≤ q ≤ 500000) — the number of operations to perform. Each of the following q lines contains two space-separated numbers ci (1 ≤ ci ≤ 3), vi (1 ≤ vi ≤ n), where ci is the operation type (according to the numbering given in the statement), and vi is the vertex on which the operation is performed.
  • It is guaranteed that the given graph is a tree.
  • 第一行为一个整数n(1<=n<=500000),为树的节点数;
  • 下面的n-1行为两个空格隔开的整数ai,bi(1<=ai, bi<=n),为树的边;
  • 下一行为一个整数q(1<=q<=500000),为操作数;接下来q行,两个空格隔开的整数ci(1<=ci<=3),vi(1<=vi<=n),其中ci为操作类型(已给出),vi为被操作的节点.
    这意味着给出的图为一棵树.

Output

  • For each type 3 operation print 1 on a separate line if the vertex is full, and 0 if the vertex is empty. Print the answers to queries in the order in which the queries are given in the input.
  • 对于每一次操作3,如果节点v充满水,单独输出一行1,如果节点v为空,单独输出一行0. 按照操作输入的顺序输出.

Sample

Input

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

Output

0
0
0
1
0
1
0
1

Solution

  • 本题我们可以考虑将树形结构转为线性结构,通过DFS序记录每个节点进入和退出的时间,则区间[ intime[v], outtime[v] ]就代表线段上从v到v的所有子节点.
  • 如何维护?对于操作1,我们要对子树区间进行区间增加操作,对于操作2,也有类似的操作;操作3是查询操作,再加上上文中记录的线性结构,可以想到用线段树来维护.
  • 下面解释一些细节:
  1. 操作1,可以对区间实现整体加和操作,线段树的函数实现不再赘述。需要注意的是,对于节点的和,应当执行sum=length,而不应该执行+=操作,否则可能出现节点上的数字大于1的情况.
  2. 操作2,需要对节点及其到根节点的路径置为0,但我们没有记录路径,相应的修改比较麻烦,我们可以考虑先进行单点修改。
    为什么单点修改?
  • 我们可以对每个节点进行query操作,若结果<length,则意味着子节点中一定存在0,此时将其父节点置为0,因此单点修改可以对其祖先产生影响.从而保证了沿路径所有节点都可以在其子节点被访问时被修改.
  1. 查询时,若子节点中有0,则该节点为0.若子节点全为1,则该节点为1.
    初始状态如何处理?
  • 初始时,所有节点全为0,但这并不影响节点的状态,如果一个节点的子节点中有0,说明该子节点没有被修改过,因此我们查询的节点也一定没有被修改过.
  1. 关于lazy tag
  • 我们可以定义lazy的三种状态,0,1,-1,在pushdown时,如果遇到-1,代表没有操作,退出;对于另外两种状态,将子节点的sum值置为lazy*length即可.
  • 其他的一些操作大体和线段树类似.

  • 代码如下:

    #include<iostream>
    #include<cstdio>
    #define maxn 500010
    using namespace std;
    struct edge{
    int to,nxt;
    edge(){to=0;nxt=0;}
    }e[maxn<<1];
    struct node{
    int sum;
    int lazy;
    int l,r;
    int lc,rc;
    node()
    {
        lazy=-1;
        lc=rc=-1;
    }
    };
    int link[maxn],edgenum,itime[maxn],otime[maxn],fa[maxn];
    int n,op,ed,q,c,v;
    int dfscnt;
    void add(int bgn,int end)
    {
    edgenum++;
    e[edgenum].to=end;
    e[edgenum].nxt=link[bgn];
    link[bgn]=edgenum;
    }
    inline int rd()
    {
    int x=0;
    bool f=true;
    char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-')f=false;
        c=getchar();
    }
    while(c>='0'&&c<='9'){
        x=(x<<1)+(x<<3)+(c^48);
        c=getchar();
    }
    return f?x:-x;
    }
    void dfs(int cur,int f)
    {
    itime[cur]=++dfscnt;
    for(int i=link[cur];i;i=e[i].nxt)
    {
        if(e[i].to==f)continue;
        fa[e[i].to]=cur;
        dfs(e[i].to,cur);
    }
    otime[cur]=dfscnt;
    }
    node tree[maxn<<1];
    int cnt;
    int rt=cnt++;
    void pushup(int cur)
    {
    int lc=tree[cur].lc,rc=tree[cur].rc;
    tree[cur].sum=tree[lc].sum+tree[rc].sum;
    tree[cur].l=tree[lc].l;
    tree[cur].r=tree[rc].r;
    }
    void pushup2(int cur)
    {
    int lc=tree[cur].lc,rc=tree[cur].rc;
    tree[cur].sum=tree[lc].sum+tree[rc].sum;
    }
    void pushdown(int cur)
    {
    if(tree[cur].lazy==-1)return;
    int lc=tree[cur].lc;
    int rc=tree[cur].rc;
    tree[lc].sum=tree[cur].lazy*(tree[lc].r-tree[lc].l+1);
    tree[rc].sum=tree[cur].lazy*(tree[rc].r-tree[rc].l+1);
    tree[lc].lazy=tree[cur].lazy;
    tree[rc].lazy=tree[cur].lazy;
    tree[cur].lazy=-1;
    }
    void build(int l,int r,int cur){
    if(l==r){
        tree[cur].sum=0;
        tree[cur].l=tree[cur].r=l;
        return;
    }
    int mid=(l+r)>>1;
    tree[cur].lc=cnt++;
    tree[cur].rc=cnt++;
    build(l,mid,tree[cur].lc);
    build(mid+1,r,tree[cur].rc);
    pushup(cur);
    }
    void upd(int l,int r,int x,int cur)
    {
    if(tree[cur].l>=l&&tree[cur].r<=r)
    {
        tree[cur].sum=x*(tree[cur].r-tree[cur].l+1);
        tree[cur].lazy=x;
        return;
    }
    pushdown(cur);
    int mid=(tree[cur].l+tree[cur].r)>>1;
    if(l<=mid)upd(l,r,x,tree[cur].lc);
    if(r>mid)upd(l,r,x,tree[cur].rc);
    pushup2(cur);
    }
    int query(int l,int r,int cur)
    {
    if(tree[cur].l>=l&&tree[cur].r<=r)
        return tree[cur].sum;
    pushdown(cur);
    int mid=(tree[cur].l+tree[cur].r)>>1;
    int tot=0;
    if(l<=mid)tot+=query(l,r,tree[cur].lc);
    if(r>mid)tot+=query(l,r,tree[cur].rc);
    return tot;
    }
    int main()
    {
    n=rd();
    for(int i=1;i<n;++i){
        op=rd();
        ed=rd();
        add(op,ed);
        add(ed,op);
    }
    dfs(1,0);
    build(1,n,rt);
    q=rd();
    for(int i=1;i<=q;++i){
        c=rd();
        if(c==1){
            v=rd();
            int l=itime[v];
            int r=otime[v];
            if(query(l,r,rt)<r-l+1&&v!=1)upd(itime[fa[v]],itime[fa[v]],0,rt);
            upd(itime[v],otime[v],1,rt);
        }
        else if (c==2){
            v=rd();
            upd(itime[v],itime[v],0,rt);
        }
        else if(c==3){
            v=rd();
            int l=itime[v];
            int r=otime[v];
            if(query(l,r,rt)<r-l+1)printf("0\n");
            else printf("1\n");
        }
    }
    return 0;
    } 

Codeforces 343D WaterTree - 线段树, DFS序的更多相关文章

  1. Tsinsen A1505. 树(张闻涛) 倍增LCA,可持久化线段树,DFS序

    题目:http://www.tsinsen.com/A1505 A1505. 树(张闻涛) 时间限制:1.0s   内存限制:512.0MB    总提交次数:196   AC次数:65   平均分: ...

  2. BZOJ_3252_攻略_线段树+dfs序

    BZOJ_3252_攻略_线段树+dfs序 Description 题目简述:树版[k取方格数] 众所周知,桂木桂马是攻略之神,开启攻略之神模式后,他可以同时攻略k部游戏.今天他得到了一款新游戏< ...

  3. 【XSY2534】【BZOJ4817】树点涂色 LCT 倍增 线段树 dfs序

    题目大意 ​ Bob有一棵\(n\)个点的有根树,其中\(1\)号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同.定义一条路径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜 ...

  4. 【bzoj4817】树点涂色 LCT+线段树+dfs序

    Description Bob有一棵n个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同.定义一条路 径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色. ...

  5. S - Query on a tree HDU - 3804 线段树+dfs序

    S - Query on a tree HDU - 3804   离散化+权值线段树 题目大意:给你一棵树,让你求这棵树上询问的点到根节点直接最大小于等于val的长度. 这个题目和之前写的那个给你一棵 ...

  6. HDU 5692 线段树+dfs序

    Snacks Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Sub ...

  7. Codeforces 343D Water Tree(DFS序 + 线段树)

    题目大概说给一棵树,进行以下3个操作:把某结点为根的子树中各个结点值设为1.把某结点以及其各个祖先值设为0.询问某结点的值. 对于第一个操作就是经典的DFS序+线段树了.而对于第二个操作,考虑再维护一 ...

  8. Codeforces 571D - Campus(并查集+线段树+DFS 序,hot tea)

    Codeforces 题目传送门 & 洛谷题目传送门 看到集合的合并,可以本能地想到并查集. 不过这题的操作与传统意义上的并查集不太一样,传统意义上的并查集一般是用来判断连通性的,而此题还需支 ...

  9. Codeforces 877E - Danil and a Part-time Job 线段树+dfs序

    给一个有根树,1e5个节点,每个节点有权值0/.1,1e5操作:1.将一个点的子树上所有点权值取反2.查询一个点的子树的权值和   题解: 先深搜整颗树,用dfs序建立每个点对应的区间,等于把树拍扁成 ...

随机推荐

  1. 云主机和vps的区别

    云主机和vps的区别   近年来,IT行业开始热衷于各种云概念,而云主机就是其中之一,并且有越来越热之势.对普通用户而言,可能不太清楚云主机和VPS的区别,下面我们就来说说云主机和VPS到底有什么不同 ...

  2. python爬微信公众号前10篇历史文章(5)-JSON相关内容小结

    json - JSON encoder and decoder JSON: JavaScript object notation,是一种轻量级的数据交换格式.JSON 是 JS 对象的字符串表示法,它 ...

  3. Shiro【授权过滤器、与ehcache整合、验证码、记住我】

    前言 本文主要讲解的知识点有以下: Shiro授权过滤器使用 Shiro缓存 与Ehcache整合 Shiro应用->实现验证码功能 记住我功能 一.授权过滤器测试 我们的授权过滤器使用的是pe ...

  4. python全栈开发-Day5 集合

    python全栈开发-Day5 集合 一.首先按照以下几个点展开对集合的学习 #一:基本使用 1 .用途 2 .定义方式 3 .常用操作+内置的方法 #二:该类型总结 1. 存一个值or存多个值 只能 ...

  5. macOS下利用dSYM文件将crash文件中的内存地址转换为可读符号

    一.使用流程 Windows下的程序运行崩溃时,往往可以利用pdb文件快速解析出程序崩溃的具体位置,甚至可以对应到源代码的具体行数.macOS下的symbolicatecrash也具备相应的功能.对应 ...

  6. Spark ML源码分析之二 从单机到分布式

            前一节从宏观角度给大家介绍了Spark ML的设计框架(链接:http://www.cnblogs.com/jicanghai/p/8570805.html),本节我们将介绍,Spar ...

  7. Mycat 分片规则详解--范围取模分片

    实现方式:该算法先进行范围分片,计算出分片组,组内在取模 优点:综合了范围分片和取模分片的优点,分片组内使用取模可以保证组内的数据分布比较均匀,分片组之间采用范围分片可以兼顾范围分片的特点,事先规划好 ...

  8. poj 3620

    题意:给出一个矩阵,其中有些格子干燥.有些潮湿. 如果一个潮湿的格子的相邻的四个方向有格子也是潮湿的,那么它们就可以构成更大 的湖泊,求最大的湖泊. 也就是求出最大的连在一块儿的潮湿的格子的数目. # ...

  9. Python爬取百度贴吧

    from urllib import request,parseimport os#找到借口及关键字base_url = 'http://tieba.baidu.com/f?'a = input(&q ...

  10. javaScript设计模式 -- 灵活的javaScript语言

    因为好长时间的懒惰和懈怠,好久没有更新文章了,从现在开始我会按时更新一些自己总结的一些知识,和研究的东西,希望能让大家从我这里学到一点点的知识. 本文参考了张荣铭的javascript设计模式一书,算 ...