题意:
一个n个结点的有根无向树,给一个结点染色就相当于给这个结点及其子树染色。共进行m次染色和查询操作。
n <= 5e4 , m <= 5e4。
题解:
1.把树转换为线段:通过dfs把每个结点标记一个遍历到该结点的时间,类似于Tarjan。然后每个结点及其子树的时间都是一段连续的区间。并且统计出每个结点及其子树的数目。第x个结点对应的位置为pos[x],第x个结点及其子树共有cnt[x]个,第x个结点及其子树所在区间为[pos[x] , pos[x] + cnt[x]]。
2.注意:遍历的时候把[l,r]区间染成的颜色为y1,需记录当时时刻t1,且无需往子线段传递这个信息。当在t2时刻把[l,r]的某一子线段染成y2时,若t1 < t2,则查询时[l,r]的颜色传递不到子线段,反之传递给子线段。
#include<bits/stdc++.h>
#define N 50005
#define inf 0x3f3f3f3f
using namespace std ;
int n ;
int color[N << 2] ;
int pos[N] , cnt[N] ;
bool in[N] ;
vector <int> edge[N] ;
int m ;
int change[N << 2] ;
int change_num ;
int x , y ;
void init()
{
int i ;
memset(in , 0 , sizeof(in)) ;
memset(pos , 0 , sizeof(pos)) ;
memset(cnt , 0 , sizeof(cnt)) ;
memset(color , -1 , sizeof(color)) ;
memset(change , -1 , sizeof(change)) ;
m = 0 ;
change_num = 0 ;
for(i = 1 ; i <= n ; i ++)
edge[i].clear() ;
}
int dfs(int u)
{
int i , j ;
int v ;
m ++ ;
pos[u] = m ;
cnt[u] = 1 ;
for(i = 0 ; i < edge[u].size() ; i ++)
{
v = edge[u][i] ;
cnt[u] += dfs(v) ;
}
return cnt[u] ;
}
void update()
{
color[pos[x]] = y ;
change[pos[x]] = ++ change_num ;
}
int query(int u)
{
int v ;
int i , j ;
if(u == x)
return color[pos[x]] ;
for(i = 0 ; i < edge[u].size() ; i ++)
{
v = edge[u][i] ;
if(pos[v] <= pos[x] && pos[x] <= pos[v] + cnt[v] - 1)
{
if(change[pos[u]] > change[pos[v]])
{
color[pos[v]] = color[pos[u]] ;
change[pos[v]] = change[pos[u]] ;
}
return query(v) ;
}
}
}
int main()
{
int t , case_t = 0 ;
int i , j ;
int u , v ;
int root ;
int num ;
char s[10] ;
scanf("%d" , &t) ;
while(t --)
{
scanf("%d" , &n) ;
init() ;
for(i = 1 ; i <= n - 1 ; i ++)
{
scanf("%d%d" , &u , &v) ;
edge[v].push_back(u) ;
in[u] = 1 ;
}
for(i = 1 ; i <= n ; i ++)
if(in[i] == 0)
{
root = i ;
dfs(i) ;
break ;
}
scanf("%d" , &num) ;
printf("Case #%d:\n" , ++ case_t) ;
while(num --)
{
scanf("%s" , s) ;
if(s[0] == 'C')
{
scanf("%d" , &x) ;
printf("%d\n" , query(root)) ;
}
else
{
scanf("%d%d" , &x , &y) ;
update() ;
}
}
}
}