【bzoj4771】七彩樹 樹鏈的並+STL-set+DFS序+可持久化線段樹

題目描述

給定一棵n個點的有根樹,編號依次爲1到n,其中1號點是根節點。每個節點都被染上了某一種顏色,其中第i個節點的顏色爲c[i]。如果c[i]=c[j],那麼我們認爲點i和點j擁有相同的顏色。定義depth[i]爲i節點與根節點的距離,爲了方便起見,你可以認爲樹上相鄰的兩個點之間的距離爲1。站在這棵色彩斑斕的樹前面,你將面臨m個問題。每個問題包含兩個整數x和d,表示詢問x子樹裏且depth不超過depth[x]+d的所有點中出現了多少種本質不同的顏色。請寫一個程序,快速回答這些詢問。

輸入

第一行包含一個正整數T(1<=T<=500),表示測試數據的組數。
每組數據中,第一行包含兩個正整數n(1<=n<=100000)和m(1<=m<=100000),表示節點數和詢問數。
第二行包含n個正整數,其中第i個數爲c[i](1<=c[i]<=n),分別表示每個節點的顏色。
第三行包含n-1個正整數,其中第i個數爲f[i+1](1<=f[i]<i),表示節點i+1的父親節點的編號。
接下來m行,每行兩個整數x(1<=x<=n)和d(0<=d<n),依次表示每個詢問。
輸入數據經過了加密,對於每個詢問,如果你讀入了x和d,那麼真實的x和d分別是x xor last和d xor last,
其中last表示這組數據中上一次詢問的答案,如果這是當前數據的第一組詢問,那麼last=0。
輸入數據保證n和m的總和不超過500000。

輸出

對於每個詢問輸出一行一個整數,即答案。

樣例輸入

1
5 8
1 3 3 2 2
1 1 3 3
1 0
0 0
3 0
1 3
2 1
2 0
6 2
4 1

樣例輸出

1
2
3
1
1
2
1
1


題解

樹鏈的並+STL-set+DFS序+可持久化線段樹

如果沒有深度限制,那麼對於一種顏色,子樹內包含該顏色的節點爲:所有該顏色節點到根節點路徑覆蓋的所有節點,即樹鏈的並。

因此對於每一種顏色求樹鏈的並,支持鏈加操作;查詢單個點的時候就是查詢單點值。樹上差分後轉變爲單點加、子樹求和,使用DFS序轉化爲區間問題後使用數據結構維護。

那麼有深度限制呢?我們按照深度維護可持久化線段樹,第 $i$ 個版本我們只考慮深度小於等於 $i$ 的節點的影響。此時需要使用STL-set維護每個顏色的樹鏈的並。

查詢時直接查詢depth[x]+d版本對應的可持久化線段樹中,x節點子樹內的權值和即可。

時間複雜度 $O(n\log n)$ 。

#include <set>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 100010
using namespace std;
set<int> s[N];
set<int>::iterator it;
int c[N] , head[N] , to[N] , next[N] , cnt , fa[N][20] , deep[N] , log[N] , pos[N] , ref[N] , last[N] , tp;
int id[N] , sum[N << 6] , ls[N << 6] , rs[N << 6] , root[N] , tc;
inline void add(int x , int y)
{
	to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt;
}
void dfs(int x)
{
	int i;
	pos[x] = ++tp , ref[tp] = x;
	for(i = 1 ; (1 << i) <= deep[x] ; i ++ ) fa[x][i] = fa[fa[x][i - 1]][i - 1];
	for(i = head[x] ; i ; i = next[i]) fa[to[i]][0] = x , deep[to[i]] = deep[x] + 1 , dfs(to[i]);
	last[x] = tp;
}
inline int lca(int x , int y)
{
	int i;
	if(deep[x] < deep[y]) swap(x , y);
	for(i = log[deep[x] - deep[y]] ; ~i ; i -- )
		if(deep[x] - deep[y] >= (1 << i))
			x = fa[x][i];
	if(x == y) return x;
	for(i = log[deep[x]] ; ~i ; i -- )
		if(deep[x] >= (1 << i) && fa[x][i] != fa[y][i])
			x = fa[x][i] , y = fa[y][i];
	return fa[x][0];
}
bool cmp(int a , int b)
{
	return deep[a] < deep[b];
}
void update(int p , int a , int l , int r , int x , int &y)
{
	y = ++tc , sum[y] = sum[x] + a;
	if(l == r) return;
	int mid = (l + r) >> 1;
	if(p <= mid) rs[y] = rs[x] , update(p , a , l , mid , ls[x] , ls[y]);
	else ls[y] = ls[x] , update(p , a , mid + 1 , r , rs[x] , rs[y]);
}
int query(int b , int e , int l , int r , int x)
{
	if(b <= l && r <= e) return sum[x];
	int mid = (l + r) >> 1 , ans = 0;
	if(b <= mid) ans += query(b , e , l , mid , ls[x]);
	if(e > mid) ans += query(b , e , mid + 1 , r , rs[x]);
	return ans;
}
int main()
{
	int T;
	scanf("%d" , &T);
	while(T -- )
	{
		cnt = tp = tc = 0;
		int n , m , i , p = 1 , x , y , lastans = 0;
		scanf("%d%d" , &n , &m);
		for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &c[i]) , s[i].clear() , id[i] = i , head[i] = 0;
		for(i = 2 ; i <= n ; i ++ ) scanf("%d" , &x) , add(x , i) , log[i] = log[i >> 1] + 1;
		dfs(1) , sort(id + 1 , id + n + 1 , cmp);
		for(root[0] = i = 0 ; i < n ; i ++ )
		{
			if(i) root[i] = root[i - 1];
			while(p <= n && deep[id[p]] <= i)
			{
				x = y = 0;
				it = s[c[id[p]]].lower_bound(pos[id[p]]);
				if(it != s[c[id[p]]].end()) y = ref[*it];
				if(it != s[c[id[p]]].begin()) x = ref[*--it];
				update(pos[id[p]] , 1 , 1 , n , root[i] , root[i]);
				if(x) update(pos[lca(x , id[p])] , -1 , 1 , n , root[i] , root[i]);
				if(y) update(pos[lca(y , id[p])] , -1 , 1 , n , root[i] , root[i]);
				if(x && y) update(pos[lca(x , y)] , 1 , 1 , n , root[i] , root[i]);
				s[c[id[p]]].insert(pos[id[p]]) , p ++ ;
			}
		}
		while(m -- )
		{
			scanf("%d%d" , &x , &y) , x ^= lastans , y = min(deep[x] + (y ^ lastans) , n - 1);
			printf("%d\n" , lastans = query(pos[x] , last[x] , 1 , n , root[y]));
		}
	}
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章