给定一棵 \(n\) 个节点的树,根节点为 1。每个节点上有一个颜色 \(c_i\)。
\(m\) 次操作。操作有一种:\(u\) \(k:\) 询问在以 \(u\) 为根的子树中,出现次数 \(\ge k\) 的颜色有多少种。
\(2 \le n \le 10^5 , 1 \le m \le 10^5 ,1 \le c_i,k \le 10^5\)
树上启发式合并即可
#include<cstdio>
#include<vector>
using namespace std;
const int N = 1e5 + 5;
int n , m , tot , dfc;
int cnt[N] , sum[N] , siz[N] , rev[N] , dfn[N] , col[N] , h[N] , son[N] , ans[N];
struct node{
int k , id;
};
vector<node> q[N];
struct edge{
int nxt , to;
}e[N * 2];
inline void add(int x , int y){e[++tot] = edge{h[x] , y} , h[x] = tot;}
void dfs1(int x , int fa)
{
siz[x] = 1 , dfn[x] = ++dfc , rev[dfc] = x;
for(register int i = h[x]; i; i = e[i].nxt)
{
int v = e[i].to;
if (v == fa) continue;
dfs1(v , x) , siz[x] += siz[v];
son[x] = siz[son[x]] < siz[v] ? v : son[x];
}
}
void dfs2(int x , int fa , int kp)
{
for(register int i = h[x]; i; i = e[i].nxt)
{
int v = e[i].to;
if (v == fa || v == son[x]) continue;
dfs2(v , x , 0);
}
if (son[x]) dfs2(son[x] , x , 1);
++sum[++cnt[col[x]]];
for(register int i = h[x]; i; i = e[i].nxt)
{
int v = e[i].to;
if (v == fa || v == son[x]) continue;
for(register int j = dfn[v]; j <= dfn[v] + siz[v] - 1; j++)
++cnt[col[rev[j]]] , ++sum[cnt[col[rev[j]]]];
}
for(register int i = 0; i < q[x].size(); i++) ans[q[x][i].id] = sum[q[x][i].k];
if (!kp)
{
for(register int i = dfn[x]; i <= dfn[x] + siz[x] - 1; i++)
--sum[cnt[col[rev[i]]]] , --cnt[col[rev[i]]];
}
}
int main()
{
scanf("%d%d" , &n , &m);
for(register int i = 1; i <= n; i++) scanf("%d" , &col[i]);
int x , y;
for(register int i = 1; i < n; i++)
scanf("%d%d" , &x , &y) , add(x , y) , add(y , x);
for(register int i = 1; i <= m; i++)
scanf("%d%d" , &x , &y) , q[x].push_back(node{y , i});
dfs1(1 , 0) , dfs2(1 , 0 , 1);
for(register int i = 1; i <= m; i++) printf("%d\n" , ans[i]);
}
原文:https://www.cnblogs.com/leiyuanze/p/13933560.html