zoukankan      html  css  js  c++  java
  • DFS序

    DFS序

    总结:

    1、树转化为线性:将树通过dfs转化为线性结构,这就是dfs序,和树链剖分有点相似

    2、普通树转化为线段树:记录每个节点构成的树(子树)的起点和终点,起点是自己,这样每个点就构成了一个区间,然后对区间的操作就和线段树和树状数组一样了。

    3、DFS序主要用来做子树的更新,因为DFS序中子树都是连续的。时间复杂度看树的储存结构,O(1)。

    4、树的储存可以用图来存储,图的数组模拟邻接矩阵:小兵应该先武装好自己,然后再加入队伍,队长负责周转(管最前面一个)。

     

    详解:

    给定一棵n个节点的树,m次查询,每次查询需要求出某个节点深度为h的所有子节点。

    对于这个问题如果试图去对每个节点保存所有深度的子节点,在数据大的时候内存会吃不消;或者每次查询的时候去遍历一遍,当数据大的时候,时间效率会非常低。

    此时如果使用dfs序维护树结构就可以轻松地解决这个问题。

    作为预处理,首先将将树的所有节点按深度保存起来,每个深度的所有节点用一个线性结构保存,每个深度的节点相对顺序要和前序遍历一致。

    然后从树的根节点进行dfs,对于每个节点记录两个信息,一个是dfs进入该节点的时间戳in[id],另一个是dfs离开该节点的时间戳out[id]。

    最后对于每次查询,求节点v在深度h的所有子节点,只需将深度为h并且dfs进入时间戳在in[v]和out[v]之间的所有节点都求出来即可,由于对于每个深度的所有节点,相对顺序和前序遍历的顺序以致,那么他们的dfs进入时间戳也是递增的,于是可以通过二分搜索求解。

    分析

    Step 1:

    如下图,可以看到,由于普通的树并不具有区间的性质,所以在考虑使用线段树作为解题思路时,需要对给给定的数据进行转化,首先对这棵树进行一次dfs遍历,记录dfs序下每个点访问起始时间与结束时间,记录起始时间是前序遍历,结束时间是后序遍历,同时对这课树进行重标号。

    Step 2:

             如下图,DFS之后,那么树的每个节点就具有了区间的性质。



             那么此时,每个节点对应了一个区间,而且可以看到,每个节点对应的区间正好“管辖”了它子树所有节点的区间,那么对点或子树的操作就转化为了对区间的操作。

    【PS: 如果对树的遍历看不懂的话,不妨待会对照代码一步一步调试,或者在纸上模拟过程~】

    Step 3:

             这个时候,每次对节点进行更新或者查询,就是线段树和树状数组最基本的实现了…

    树是一种非线性结构,一般而言,我们总是想办法将其转化为线性结构,将树上操作包括子树操作、路径操作等转化为数组上的区间操作,从而在一个较为理想的复杂度内加以解决。将树“拍平”的方法有很多,例如欧拉序、HLD等。实际上欧拉序也是在DFS过程中得到的。不过通常而言,我们所说的DFS序是指:每个节点进出栈的时间序列

    这里写图片描述 
    考虑上图中树的DFS序,应为 
    这里写图片描述

    其中,每个节点均会出现2次,第一次是进入DFS的时刻,第二次是离开DFS的时刻。分别称之为InOut。在区间操作中,如果某个节点出现了2次,则该节点将被“抵消”。所以通常会将Out时刻对应的点设置为负数。

    树的DFS序列有几个有用的性质:

    1. 任意子树是连续的。例如子树BEFK,在序列中对应BEEFKKFB;子树CGHI,在序列中对应连续区间CGGHHIIC
    2. 任意点对(a,b)之间的路径,可分为2种情况,首先令lcaab的最近公共祖先: 
      1. lcaab之一,则ab之间的In时刻的区间或者Out时刻区间就是其路径。例如AK之间的路径就对应区间ABEEFK,或者KFBCGGHHIICA
      2. lca另有其人,则ab之间的路径为In[a]Out[b]之间的区间或者In[b]Out[a]之间的区间。另外,还需额外加上lca!!!考虑EK路径,对应为EFK再加上B。考虑EH之间的路径,对应为EFKKFBCGGH再加上A

    利用这些性质,可以利用DFS序列完成子树操作和路径操作,同时也有可能将莫队算法应用到树上从而得到树上莫队

    代码:

    dfs序是处理树上问题很重要的一个武器,主要能够解决对于一个点,它的子树上的一些信息的维护。 
    就比如那天百度之星round1A的1003题,就是dfs序+线段树维护每个点到0点的距离,然后对于每个点的更新,只需要更新它和它的子树上的点到0点的距离,查询的话就是它的子树上的最大值即可 
    我的dfs序开的空间就是n,因为只在入的地方时间戳++,出来的地方时间戳不变,线段树的每个节点应该是时间戳

     1 void dfs(int u,int fa){
     2     p1[u]=++ti;
     3     dfsnum[ti]=u;
     4     for(int i=head[u];i!=-1;i=edge[i].next){
     5         int v=edge[i].v;
     6         if(v==fa) continue;
     7         dfs(v,u);
     8     }
     9     p2[u]=ti;//时间戳不变,空间为O(n)
    10 }

    例题:

    BZOJ4034需要在树上完成3类操作,单点更新,子树更新,以及根到指定节点的路径查询。利用性质1以及性质2.1即可完成,连LCA都无需求出。对整个DFS序列使用线段树进行维护,注意到整个序列实际上有正有负,因此额外用一个域来表示正数的个数。

    4034: [HAOI2015]树上操作

    Time Limit: 10 Sec  Memory Limit: 256 MB
    Submit: 6323  Solved: 2094
    [Submit][Status][Discuss]

    Description

    有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个
    操作,分为三种:
    操作 1 :把某个节点 x 的点权增加 a 。
    操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。
    操作 3 :询问某个节点 x 到根的路径中所有点的点权和。

    Input

    第一行包含两个整数 N, M 。表示点数和操作数。接下来一行 N 个整数,表示树中节点的初始权值。接下来 N-1 
    行每行三个正整数 fr, to , 表示该树中存在一条边 (fr, to) 。再接下来 M 行,每行分别表示一次操作。其中
    第一个数表示该操作的种类( 1-3 ) ,之后接这个操作的参数( x 或者 x a ) 。
     

    Output

    对于每个询问操作,输出该询问的答案。答案之间用换行隔开。

     

    Sample Input

    5 5
    1 2 3 4 5
    1 2
    1 4
    2 3
    2 5
    3 3
    1 2 1
    3 5
    2 1 2
    3 3

    Sample Output

    6
    9
    13

    HINT

     对于 100% 的数据, N,M<=100000 ,且所有输入数据的绝对值都不会超过 10^6 。

    Source

      1 #include <cstdio>
      2 #include <climits>
      3 #include <algorithm>
      4 #include <iostream>
      5 using namespace std;
      6 
      7 int const SIZE = 100100;
      8 typedef long long weight_t;
      9 
     10 struct edge_t{//
     11     int to;
     12     int next;
     13 }Edge[SIZE<<1];//双向的  所以*2 
     14 int Vertex[SIZE];
     15 int ECnt;
     16 weight_t W[SIZE];
     17 
     18 //数组模拟邻接矩阵:顶点记录的是下一个点,顶点负责转运 
     19 inline void mkEdge(int a,int b){//造a-b这条边 
     20     //先把节点的信息补充好,再建立联系
     21     //先武装好自己,然后再图报效
     22     //每个队的长官记录每个队节点的分配信息 
     23     Edge[ECnt].to = b;
     24     Edge[ECnt].next = Vertex[a];
     25     Vertex[a] = ECnt++;
     26 
     27     Edge[ECnt].to = a;
     28     Edge[ECnt].next = Vertex[b];
     29     Vertex[b] = ECnt++;
     30 }
     31 
     32 int InIdx[SIZE],OutIdx[SIZE];
     33 int InOut[SIZE<<1];
     34 int NewIdx[SIZE<<1];
     35 int NCnt;
     36 
     37 void dfs(int node,int parent){
     38     NewIdx[NCnt] = node;
     39     InOut[NCnt] = 1;
     40     InIdx[node] = NCnt++;
     41     for(int next=Vertex[node];next;next=Edge[next].next){
     42         int son = Edge[next].to;
     43         if ( son != parent ) dfs(son,node);
     44     }
     45     NewIdx[NCnt] = node;
     46     InOut[NCnt] = -1;
     47     OutIdx[node] = NCnt++;
     48 }
     49 
     50 int N;
     51 weight_t StSum[SIZE<<3];
     52 weight_t Lazy[SIZE<<3];
     53 int Flag[SIZE<<3];//The count of the positive number in the range
     54 
     55 inline int lson(int x){return x<<1;}
     56 inline int rson(int x){return lson(x)|1;}
     57 
     58 inline void _pushUp(int t){
     59     StSum[t] = StSum[lson(t)] + StSum[rson(t)];
     60     Flag[t] = Flag[lson(t)] + Flag[rson(t)];
     61 }
     62 
     63 inline void _pushDown(int t){
     64     if ( 0LL == Lazy[t] ) return;
     65 
     66     weight_t& x = Lazy[t];
     67 
     68     int son = lson(t);
     69     StSum[son] += Flag[son] * x;
     70     Lazy[son] += x;
     71 
     72     son = rson(t);
     73     StSum[son] += Flag[son] * x;
     74     Lazy[son] += x;
     75 
     76     x = 0LL;
     77 }
     78 
     79 void build(int t,int s,int e){
     80     Lazy[t] = 0LL;
     81     if ( s == e ){
     82         StSum[t] = InOut[s] * W[NewIdx[s]];
     83         Flag[t] = InOut[s];
     84         return;
     85     }
     86 
     87     int m = ( s + e ) >> 1;
     88     build(lson(t),s,m);
     89     build(rson(t),m+1,e);
     90     _pushUp(t);
     91 }
     92 
     93 void modify(int t,int s,int e,int a,int b,weight_t delta){
     94     if ( a <= s && e <= b ){
     95         StSum[t] += Flag[t] * delta;
     96         Lazy[t] += delta;
     97         return;
     98     }
     99 
    100     _pushDown(t);
    101     int m = ( s + e ) >> 1;
    102     if ( a <= m ) modify(lson(t),s,m,a,b,delta);
    103     if ( m < b ) modify(rson(t),m+1,e,a,b,delta);
    104     _pushUp(t);
    105 }
    106 
    107 weight_t query(int t,int s,int e,int a,int b){
    108     if ( a <= s && e <= b ){
    109         return StSum[t];
    110     }
    111 
    112     _pushDown(t);
    113 
    114     weight_t ret = 0LL;
    115     int m = ( s + e ) >> 1;
    116     if ( a <= m ) ret += query(lson(t),s,m,a,b);
    117     if ( m < b ) ret += query(rson(t),m+1,e,a,b);
    118     return ret;
    119 }
    120 
    121 inline weight_t query(int x){
    122     return query(1,1,N<<1,1,InIdx[x]);
    123 }
    124 
    125 inline void modify(int x,weight_t delta){
    126     modify(1,1,N<<1,InIdx[x],InIdx[x],delta);
    127     modify(1,1,N<<1,OutIdx[x],OutIdx[x],delta);
    128 }
    129 
    130 inline void modifySubtree(int x,weight_t delta){
    131     modify(1,1,N<<1,InIdx[x],OutIdx[x],delta);
    132 }
    133 
    134 //这里树的储存方式用的是图里面的数组仿邻接矩阵 
    135 inline void initTree(int n){
    136     ECnt = NCnt = 1;
    137     //vertex是顶点,这就是存储边的方式 
    138     fill(Vertex,Vertex+n+1,0);
    139 }
    140 
    141 int M;
    142 bool read(){
    143     if ( EOF == scanf("%d%d",&N,&M) ) return false;
    144 
    145     initTree(N);
    146     for(int i=1;i<=N;++i)scanf("%lld",W+i);//读权值 
    147 
    148     int a,b;
    149     for(int i=1;i<N;++i){
    150         scanf("%d%d",&a,&b);//读每一条边 
    151         mkEdge(a,b);//造边 
    152     }
    153     cout<<"Edge[i].to"<<" "<<"Edge[i].next"<<endl;
    154     for(int i=0;i<=20;i++){
    155         cout<<Edge[i].to<<" "<<Edge[i].next<<endl;
    156     } 
    157     dfs(1,0);
    158     build(1,1,N<<1);
    159     return true;
    160 }
    161 
    162 void proc(){
    163     int cmd,x;
    164     weight_t a;
    165     while(M--){
    166         scanf("%d%d",&cmd,&x);
    167         switch(cmd){
    168         case 1:scanf("%lld",&a);modify(x,a);break;
    169         case 2:scanf("%lld",&a);modifySubtree(x,a);break;
    170         case 3:printf("%lld
    ",query(x));break;
    171         }
    172     }
    173 }
    174 int main(){
    175     freopen("in.txt","r",stdin);
    176     while( read() ) proc();
    177     return 0;
    178 }

    HDU 3887 
    题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=3887 
    问你对于每个节点,它的子树上标号比它小的点有多少个 
    子树的问题,dfs序可以很轻松的解决,因为点在它的子树上,所以在线段树中,必定在它的两个时间戳的区间之间,所以我们只需要从小到大考虑,它的区间里有多少个点已经放了,然后再把它放进去。很容易的解决了 
    代码:

      1 #include <map>
      2 #include <set>
      3 #include <stack>
      4 #include <queue>
      5 #include <cmath>
      6 #include <string>
      7 #include <vector>
      8 #include <cstdio>
      9 #include <cctype>
     10 #include <cstring>
     11 #include <sstream>
     12 #include <cstdlib>
     13 #include <iostream>
     14 #include <algorithm>
     15 #pragma comment(linker, "/STACK:102400000,102400000")
     16 
     17 using namespace std;
     18 #define   MAX           500005
     19 #define   MAXN          6005
     20 #define   maxnode       15
     21 #define   sigma_size    30
     22 #define   lson          l,m,rt<<1
     23 #define   rson          m+1,r,rt<<1|1
     24 #define   lrt           rt<<1
     25 #define   rrt           rt<<1|1
     26 #define   middle        int m=(r+l)>>1
     27 #define   LL            long long
     28 #define   ull           unsigned long long
     29 #define   mem(x,v)      memset(x,v,sizeof(x))
     30 #define   lowbit(x)     (x&-x)
     31 #define   pii           pair<int,int>
     32 #define   bits(a)       __builtin_popcount(a)
     33 #define   mk            make_pair
     34 #define   limit         10000
     35 
     36 //const int    prime = 999983;
     37 const int    INF   = 0x3f3f3f3f;
     38 const LL     INFF  = 0x3f3f;
     39 const double pi    = acos(-1.0);
     40 //const double inf   = 1e18;
     41 const double eps   = 1e-8;
     42 const LL    mod    = 1e9+7;
     43 const ull    mx    = 133333331;
     44 
     45 /*****************************************************/
     46 inline void RI(int &x) {
     47       char c;
     48       while((c=getchar())<'0' || c>'9');
     49       x=c-'0';
     50       while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0';
     51  }
     52 /*****************************************************/
     53 
     54 struct Edge{
     55     int v,next;
     56 }edge[MAX*2];
     57 int head[MAX];
     58 int tot;
     59 int p1[MAX];
     60 int p2[MAX];
     61 int ti;
     62 int sum[MAX<<2];
     63 
     64 void init(){
     65     mem(head,-1);
     66     tot=0;ti=0;
     67 }
     68 
     69 void add_edge(int a,int b){
     70     edge[tot]=(Edge){b,head[a]};
     71     head[a]=tot++;
     72 }
     73 
     74 void dfs(int u,int fa){
     75     p1[u]=++ti;
     76     for(int i=head[u];i!=-1;i=edge[i].next){
     77         int v=edge[i].v;
     78         if(v==fa) continue;
     79         dfs(v,u);
     80     }
     81     p2[u]=ti;
     82 }
     83 
     84 void build(int l,int r,int rt){
     85     sum[rt]=0;
     86     if(l==r) return;
     87     middle;
     88     build(lson);
     89     build(rson);
     90 }
     91 
     92 void pushup(int rt){
     93     sum[rt]=sum[lrt]+sum[rrt];
     94 }
     95 
     96 void update(int l,int r,int rt,int pos,int d){
     97     if(l==r){
     98         sum[rt]+=d;
     99         return;
    100     }
    101     middle;
    102     if(pos<=m) update(lson,pos,d);
    103     else update(rson,pos,d);
    104     pushup(rt);
    105 }
    106 
    107 int query(int l,int r,int rt,int L,int R){
    108     if(L<=l&&r<=R) return sum[rt];
    109     middle;
    110     int ret=0;
    111     if(L<=m) ret+=query(lson,L,R);
    112     if(R>m) ret+=query(rson,L,R);
    113     return ret;
    114 }
    115 
    116 int main(){
    117     int n,p;
    118     while(cin>>n>>p&&n){
    119         init();
    120         for(int i=1;i<n;i++){
    121             int a,b;
    122             scanf("%d%d",&a,&b);
    123             add_edge(a,b);
    124             add_edge(b,a);
    125         }
    126         dfs(p,-1);
    127         build(1,n,1);
    128         for(int i=1;i<=n;i++){
    129             printf("%d",query(1,n,1,p1[i],p2[i]));
    130             if(i==n) printf("
    ");
    131             else printf(" ");
    132             update(1,n,1,p1[i],1);
    133         }
    134     }
    135     return 0;
    136 }

    poj 3321 
    题目传送门:http://poj.org/problem?id=3321 
    这题是一开始告诉你树上每个节点都有1个苹果,然后你对一个节点操作,如果有苹果,就拿走,没苹果,就放上,然后询问你以x为根的子树上有多少个苹果。 
    dfs序水题,代码:

      1 #include <map>
      2 #include <set>
      3 #include <stack>
      4 #include <queue>
      5 #include <cmath>
      6 #include <string>
      7 #include <vector>
      8 #include <cstdio>
      9 #include <cctype>
     10 #include <cstring>
     11 #include <sstream>
     12 #include <cstdlib>
     13 #include <iostream>
     14 #include <algorithm>
     15 #pragma comment(linker, "/STACK:102400000,102400000")
     16 
     17 using namespace std;
     18 #define   MAX           500005
     19 #define   MAXN          6005
     20 #define   maxnode       15
     21 #define   sigma_size    30
     22 #define   lson          l,m,rt<<1
     23 #define   rson          m+1,r,rt<<1|1
     24 #define   lrt           rt<<1
     25 #define   rrt           rt<<1|1
     26 #define   middle        int m=(r+l)>>1
     27 #define   LL            long long
     28 #define   ull           unsigned long long
     29 #define   mem(x,v)      memset(x,v,sizeof(x))
     30 #define   lowbit(x)     (x&-x)
     31 #define   pii           pair<int,int>
     32 #define   bits(a)       __builtin_popcount(a)
     33 #define   mk            make_pair
     34 #define   limit         10000
     35 
     36 //const int    prime = 999983;
     37 const int    INF   = 0x3f3f3f3f;
     38 const LL     INFF  = 0x3f3f;
     39 const double pi    = acos(-1.0);
     40 //const double inf   = 1e18;
     41 const double eps   = 1e-8;
     42 const LL    mod    = 1e9+7;
     43 const ull    mx    = 133333331;
     44 
     45 /*****************************************************/
     46 inline void RI(int &x) {
     47       char c;
     48       while((c=getchar())<'0' || c>'9');
     49       x=c-'0';
     50       while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0';
     51  }
     52 /*****************************************************/
     53 
     54 struct Edge{
     55     int v,next;
     56 }edge[MAX*2];
     57 int head[MAX];
     58 int tot;
     59 int p1[MAX];
     60 int p2[MAX];
     61 int ti;
     62 int sum[MAX<<2];
     63 
     64 void init(){
     65     mem(head,-1);
     66     tot=0;ti=0;
     67 }
     68 
     69 void add_edge(int a,int b){
     70     edge[tot]=(Edge){b,head[a]};
     71     head[a]=tot++;
     72 }
     73 
     74 void dfs(int u,int fa){
     75     p1[u]=++ti;
     76     for(int i=head[u];i!=-1;i=edge[i].next){
     77         int v=edge[i].v;
     78         if(v==fa) continue;
     79         dfs(v,u);
     80     }
     81     p2[u]=ti;
     82 }
     83 
     84 void pushup(int rt){
     85     sum[rt]=sum[lrt]+sum[rrt];
     86 }
     87 
     88 void build(int l,int r,int rt){
     89     if(l==r){
     90         sum[rt]=1;
     91         return;
     92     }
     93     middle;
     94     build(lson);
     95     build(rson);
     96     pushup(rt);
     97 }
     98 
     99 void update(int l,int r,int rt,int pos){
    100     if(l==r){
    101         sum[rt]=sum[rt]^1;
    102         return;
    103     }
    104     middle;
    105     if(pos<=m) update(lson,pos);
    106     else update(rson,pos);
    107     pushup(rt);
    108 }
    109 
    110 int query(int l,int r,int rt,int L,int R){
    111     if(L<=l&&r<=R) return sum[rt];
    112     middle;
    113     int ret=0;
    114     if(L<=m) ret+=query(lson,L,R);
    115     if(R>m) ret+=query(rson,L,R);
    116     return ret;
    117 }
    118 
    119 int main(){
    120     int n;
    121     cin>>n;
    122     init();
    123     for(int i=1;i<n;i++){
    124         int a,b;
    125         scanf("%d%d",&a,&b);
    126         add_edge(a,b);
    127         add_edge(b,a);
    128     }
    129     dfs(1,-1);
    130     build(1,n,1);
    131     int m;
    132     scanf("%d",&m);
    133     while(m--){
    134         char op;
    135         int a;
    136         getchar();
    137         scanf("%c%d",&op,&a);
    138         if(op=='Q') printf("%d
    ",query(1,n,1,p1[a],p2[a]));
    139         else update(1,n,1,p1[a]);
    140     }
    141     return 0;
    142 }

    CodeForces 620E 
    题目传送门:http://codeforces.com/problemset/problem/620/E 
    给你一棵树,每个节点都有颜色,然后问你子树上有多少种不同的颜色 
    考虑到颜色一共只有60种,所以可以直接二进制记录,每个节点记录这个节点的颜色,然后区间直接左右子树或起来,然后对x为根的子树都变成c颜色,区间赋值,需要pushdown,pushup。 
    有个坑点就是需要记录每个时间戳是哪个点,然后在build线段树的时候,sum[rt]=c[dfsnum[l]],这个坑点我错了好久,因为线段树的节点的下标应该是时间戳。GG,仍需努力 
    代码:

      1 #include <map>
      2 #include <set>
      3 #include <stack>
      4 #include <queue>
      5 #include <cmath>
      6 #include <string>
      7 #include <vector>
      8 #include <cstdio>
      9 #include <cctype>
     10 #include <cstring>
     11 #include <sstream>
     12 #include <cstdlib>
     13 #include <iostream>
     14 #include <algorithm>
     15 #pragma comment(linker, "/STACK:102400000,102400000")
     16 
     17 using namespace std;
     18 #define   MAX           400005
     19 #define   MAXN          6005
     20 #define   maxnode       15
     21 #define   sigma_size    30
     22 #define   lson          l,m,rt<<1
     23 #define   rson          m+1,r,rt<<1|1
     24 #define   lrt           rt<<1
     25 #define   rrt           rt<<1|1
     26 #define   middle        int m=(r+l)>>1
     27 #define   LL            long long
     28 #define   ull           unsigned long long
     29 #define   mem(x,v)      memset(x,v,sizeof(x))
     30 #define   lowbit(x)     (x&-x)
     31 #define   pii           pair<int,int>
     32 #define   bits(a)       __builtin_popcount(a)
     33 #define   mk            make_pair
     34 #define   limit         10000
     35 
     36 //const int    prime = 999983;
     37 const int    INF   = 0x3f3f3f3f;
     38 const LL     INFF  = 0x3f3f;
     39 const double pi    = acos(-1.0);
     40 //const double inf   = 1e18;
     41 const double eps   = 1e-8;
     42 const LL    mod    = 1e9+7;
     43 const ull    mx    = 133333331;
     44 
     45 /*****************************************************/
     46 inline void RI(int &x) {
     47       char c;
     48       while((c=getchar())<'0' || c>'9');
     49       x=c-'0';
     50       while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0';
     51  }
     52 /*****************************************************/
     53 
     54 struct Edge{
     55     int v,next;
     56 }edge[MAX*2];
     57 int head[MAX];
     58 int tot;
     59 int c[MAX];
     60 int p1[MAX];
     61 int p2[MAX];
     62 int ti;
     63 int dfsnum[MAX];
     64 LL sum[MAX<<2];
     65 int col[MAX<<2];
     66 void init(){
     67     mem(head,-1);
     68     tot=0;ti=0;
     69 }
     70 
     71 void add_edge(int a,int b){
     72     edge[tot]=(Edge){b,head[a]};
     73     head[a]=tot++;
     74 }
     75 
     76 void dfs(int u,int fa){
     77     p1[u]=++ti;
     78     dfsnum[ti]=u;
     79     for(int i=head[u];i!=-1;i=edge[i].next){
     80         int v=edge[i].v;
     81         if(v==fa) continue;
     82         dfs(v,u);
     83     }
     84     p2[u]=ti;
     85 }
     86 
     87 void pushup(int rt){
     88     sum[rt]=sum[lrt]|sum[rrt];
     89 }
     90 
     91 void pushdown(int rt){
     92     if(col[rt]){
     93         col[lrt]=col[rrt]=col[rt];
     94         sum[lrt]=sum[rrt]=(1LL<<col[rt]);
     95         col[rt]=0;
     96     }
     97 }
     98 void build(int l,int r,int rt){
     99     col[rt]=0;
    100     if(l==r){
    101         sum[rt]=(1LL<<c[dfsnum[l]]);
    102         return;
    103     }
    104     middle;
    105     build(lson);
    106     build(rson);
    107     pushup(rt);
    108 }
    109 
    110 void update(int l,int r,int rt,int L,int R,int d){
    111     if(L<=l&&r<=R){
    112         sum[rt]=(1LL<<d);
    113         col[rt]=d;
    114         return;
    115     }
    116     middle;
    117     pushdown(rt);
    118     if(L<=m) update(lson,L,R,d);
    119     if(R>m) update(rson,L,R,d);
    120     pushup(rt);
    121 }
    122 
    123 LL query(int l,int r,int rt,int L,int R){
    124     if(L<=l&&r<=R) return sum[rt];
    125     middle;
    126     LL ret=0;
    127     pushdown(rt);
    128     if(L<=m) ret|=query(lson,L,R);
    129     if(R>m) ret|=query(rson,L,R);
    130     return ret;
    131 }
    132 
    133 int main(){
    134     //freopen("in.txt","r",stdin);
    135     int n,m;
    136     while(cin>>n>>m){
    137         init();
    138         for(int i=1;i<=n;i++) scanf("%d",&c[i]);
    139         for(int i=1;i<n;i++){
    140             int a,b;
    141             scanf("%d%d",&a,&b);
    142             add_edge(a,b);
    143             add_edge(b,a);
    144         }
    145         dfs(1,-1);
    146         build(1,n,1);
    147         //cout<<query(1,n,1,p1[6],p2[6]);
    148         while(m--){
    149             int op,a;
    150             scanf("%d%d",&op,&a);
    151             if(op==1){
    152                 int cc;
    153                 scanf("%d",&cc);
    154                 update(1,n,1,p1[a],p2[a],cc);
    155             }
    156             else {
    157                 LL k=query(1,n,1,p1[a],p2[a]);
    158                 //cout<<k<<" ";
    159                 int num=0;
    160                 while(k){
    161                     int tmp=k%2;
    162                     k/=2;
    163                     num+=tmp;
    164                     //if(tmp) cout<<tmp<<" ";
    165                 }
    166                 printf("%d
    ",num);
    167             }
    168         }
    169     }
    170     return 0;
    171 }
  • 相关阅读:
    AWS的EC2实例搭建服务器使用stackoverflow教程
    亚马逊云开服之旅
    Linux主机之间ssh免密登录配置方法
    阿里云运维培训讲解
    区块链技术在物流领域应用分析
    公众号页面分享朋友圈后点击跳转到小程序的方法介绍
    数据库主库从库宕机重启后binlog数据同步
    Docker+ElasticSearch+Logstash+Kibana+Filebeat搭建方法
    linux实现磁盘自动挂载脚本
    阿里云ecs基于镜像进行旧服务器迁移到新服务器
  • 原文地址:https://www.cnblogs.com/Renyi-Fan/p/8244003.html
Copyright © 2011-2022 走看看