BZOj #4771. 七彩树
- description
- solution
- code
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
序,然后就可以用线段树维护,变成区间问题
询问距离不超过depx+ddep_x+ddepx+d,那就按深度排序后,建主席树
则对于深度为iii的版本的线段树只会存在深度≤i\le i≤i的点
线段树上的点管辖的区间就是其子树
其点存的值,就是管辖区间内不同颜色的个数
最后只需要处理子树内多个点有同样颜色的修改
具体而言,根据dfn
序,对于新增点iii,在线段树上对应的点进行+1
找到与其颜色相同的dfn
小于iii的最大dfn
序对应的点,记为iii的前驱
那么求出这两个点的lca
,从lca
往上的点都含有这两个点,但只需要算一个,所以在线段树上lca
对应的叶子节点,进行-1
同理。找到与其颜色相同的dfn
大于iii的最小dfn
序对应的点,记为iii的后继
那么求出这两个点的lca
,从lca
往上的点都含有这两个点,但只需要算一个,所以在线段树上lca
对应的叶子节点,进行-1
但是前驱和后继的lca
又多减了111,所以要对其进行+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;
}