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的時刻。分別稱之為In 與Out 。在區間操作中,如果某個節點出現了2次,則該節點將被“抵消”。所以通常會將Out 時刻對應的點設置為負數。
樹的DFS序列有幾個有用的性質:
任意子樹是連續的。例如子樹BEFK ,在序列中對應BEEFKKFB ;子樹CGHI ,在序列中對應連續區間CGGHHIIC 。 任意點對(a,b) 之間的路徑,可分為2種情況,首先令lca 是a 、b 的最近公共祖先:? 若lca 是a 、b 之一,則a 、b 之間的In 時刻的區間或者Out 時刻區間就是其路徑。例如AK 之間的路徑就對應區間ABEEFK ,或者KFBCGGHHIICA 。 若lca 另有其人,則a 、b 之間的路徑為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 MBSubmit:?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 鳴謝bhiaibogf提供
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\n " ,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(
" \n " );
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\n " ,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\n " ,num);
167 }
168 }
169 }
170 return 0 ;
171 }
?
轉載于:https://www.cnblogs.com/Renyi-Fan/p/8244003.html
總結
以上是生活随笔 為你收集整理的DFS序 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。