BZOj #4771. 七彩树
description
给定一棵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的所有点中出现了多少种本质不同的颜色。请写一个程序,快速回答这些询问。
Input
第一行包含一个正整数T(1<=T<=500),表示测试数据的组数。
每组数据中,第一行包含两个正整数n(1<=n<=100000)和m(1<=m<=100000),表示节点数和询问数。
第二行包含n个正整数,其中第i个数为 c [ i ] ( 1 < = c [ i ] < = n ) c[i](1<=c[i]<=n) c[i](1<=c[i]<=n),分别表示每个节点的颜色。
第三行包含n-1个正整数,其中第i个数为 f [ i + 1 ] ( 1 < = f [ i ] < i ) f[i+1](1<=f[i]<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。
Output
对于每个询问输出一行一个整数,即答案。
Sample Input
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
Sample Output
1
2
3
1
1
2
1
1
solution
询问子树内的问题,通常都转化成dfn
序,然后就可以用线段树维护,变成区间问题
询问距离不超过 d e p x + d dep_x+d depx+d,那就按深度排序后,建主席树
则对于深度为 i i i的版本的线段树只会存在深度 ≤ i \le i ≤i的点
线段树上的点管辖的区间就是其子树
其点存的值,就是管辖区间内不同颜色的个数
最后只需要处理子树内多个点有同样颜色的修改
具体而言,根据dfn
序,对于新增点
i
i
i,在线段树上对应的点进行+1
找到与其颜色相同的dfn
小于
i
i
i的最大dfn
序对应的点,记为
i
i
i的前驱
那么求出这两个点的lca
,从lca
往上的点都含有这两个点,但只需要算一个,所以在线段树上lca
对应的叶子节点,进行-1
同理。找到与其颜色相同的dfn
大于
i
i
i的最小dfn
序对应的点,记为
i
i
i的后继
那么求出这两个点的lca
,从lca
往上的点都含有这两个点,但只需要算一个,所以在线段树上lca
对应的叶子节点,进行-1
但是前驱和后继的lca
又多减了
1
1
1,所以要对其进行+1
操作
code
#include <set>
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
#define maxn 100005
set < int > s[maxn];
struct node { int id, dep; }t[maxn];
int T, n, m, cnt;
int root[maxn], c[maxn], dep[maxn], dfn[maxn], St[maxn], Ed[maxn], mp[maxn], head[maxn], to[maxn], nxt[maxn];
int f[maxn][20];
void dfs( int u ) {
dep[u] = dep[f[u][0]] + 1;
dfn[u] = St[u] = ++ cnt;
mp[cnt] = u;
for( int i = 1;i < 20;i ++ )
f[u][i] = f[f[u][i - 1]][i - 1];
for( int i = head[u];i;i = nxt[i] ) dfs( to[i] );
Ed[u] = cnt;
}
struct Node { int lson, rson, sum; }tree[maxn * 50];
void modify( int &now, int lst, int l, int r, int pos, int k ) {
tree[now = ++ cnt] = tree[lst];
tree[now].sum += k;
if( l == r ) return;
int mid = ( l + r ) >> 1;
if( pos <= mid ) modify( tree[now].lson, tree[lst].lson, l, mid, pos, k );
else modify( tree[now].rson, tree[lst].rson, mid + 1, r, pos, k );
}
int query( int now, int l, int r, int L, int R ) {
if( r < L or R < l ) return 0;
if( L <= l and r <= R ) return tree[now].sum;
int mid = ( l + r ) >> 1;
return query( tree[now].lson, l, mid, L, R ) + query( tree[now].rson, mid + 1, r, L, R );
}
int lca( int u, int v ) {
if( dep[u] < dep[v] ) swap( u, v );
for( int i = 19;~ i;i -- ) if( dep[f[u][i]] >= dep[v] ) u = f[u][i];
if( u == v ) return u;
for( int i = 19;~ i;i -- ) if( f[u][i] ^ f[v][i] ) u = f[u][i], v = f[v][i];
return f[u][0];
}
int main() {
scanf( "%d", &T );
while( T -- ) {
scanf( "%d %d", &n, &m );
for( int i = 1;i <= n;i ++ )
head[i] = 0, s[i].clear();
for( int i = 1;i <= n;i ++ )
scanf( "%d", &c[i] );
cnt = 1;
for( int i = 2;i <= n;i ++ ) {
scanf( "%d", &f[i][0] );
to[cnt] = i, nxt[cnt] = head[f[i][0]], head[f[i][0]] = cnt ++;
}
cnt = 0;
dfs( 1 );
for( int i = 1;i <= n;i ++ )
t[i].id = i, t[i].dep = dep[i];
sort( t + 1, t + n + 1, []( node x, node y ) { return x.dep < y.dep; } );
cnt = 0;
for( int i = 1;i <= n;i ++ ) {
root[t[i].dep] = root[t[i - 1].dep];
int id = t[i].id, d = t[i].dep;
modify( root[d], root[d], 1, n, dfn[id], 1 );
auto it = s[c[id]].lower_bound( dfn[id] );
int pre = -1, nxt = -1;
if( it != s[c[id]].end() ) {
nxt = *it;
modify( root[d], root[d], 1, n, dfn[lca( id, mp[nxt] )], -1 );
}
if( it != s[c[id]].begin() ) {
pre = * (-- it);
modify( root[d], root[d], 1, n, dfn[lca( id, mp[pre] )], -1 );
}
if( ~ pre and ~ nxt )
modify( root[d], root[d], 1, n, dfn[lca( mp[pre], mp[nxt] )], 1 );
s[c[id]].insert( dfn[id] );
}
int last = 0, x, d;
while( m -- ) {
scanf( "%d %d", &x, &d );
x ^= last, d ^= last;
printf( "%d\n", last = query( root[dep[x] + d], 1, n, St[x], Ed[x] ) );
}
cnt = 0;
}
return 0;
}