传送门
感觉挺套路的吧。给出一棵树,然后查询子树里面出现次数大于等于k的颜色个数。
因为是求子树问题,看一眼就知道是用dfs序把树变成一个序列。
然后统计就用莫队去求。
开始我憨批地想维护下前缀和,然后发现树状数组是只改变后缀和的,然后改了强制把树状数组维护成了前缀和。
但发现好像多了一个数字,记录下sum[cnt[a[x]]]++
,不就行了吗。
然后发现我树状数组的两个操作就是这个意思。
没啥好说的,就是个套路题。看一眼就知道的题。
但注意下树上莫队是记录树上任意两点,而莫队+dfs序是记录子树的问题。
#include <bits/stdc++.h>
using namespace std;
template<typename T = long long> inline T read() {
T s = 0, f = 1; char ch = getchar();
while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
while(isdigit(ch)) {s = (s << 3) + (s << 1) + ch - 48; ch = getchar();}
return s * f;
}
const int N = 1e5 + 5, M = 1e6 + 5, MOD = 1e9 + 7, CM = 998244353, INF = 0x3f3f3f3f;
int a[N], col[N];
struct Edge{
int to, next;
}e[M << 1];
int head[N], tot;
void add(int u, int v){
e[++tot].to = v;
e[tot].next = head[u];
head[u] = tot;
}
int l[N], r[N], id, bl[N];
void dfs(int u, int fath){
l[u] = ++id;
for(int i = head[u]; i; i = e[i].next) {
int v = e[i].to;
if(v == fath) continue;
dfs(v, u);
}
r[u] = id;
}
struct Query{
int l, r, id, k;
} q[N];
bool cmp(const Query &a, const Query &b) {
return (bl[a.l] ^ bl[b.l]) ? bl[a.l] < bl[b.l] : ((bl[a.l] & 1) ? a.r < b.r : a.r > b.r);
}
int nowl = 1, nowr = 0, cnt[N], Ans[N], sum[N];
void add(int x) {
++cnt[a[x]];
if(cnt[a[x]] >= 0) sum[cnt[a[x]]]++;
}
void del(int x) {
if(cnt[a[x]] >= 0) sum[cnt[a[x]]]--;
--cnt[a[x]];
}
int main(){
int n = read(), m = read();
for(int i = 1; i <= n; i++) col[i] = read();
for(int i = 1; i < n; i++) {
int u = read(), v = read();
add(u, v), add(v, u);
}
dfs(1, 0);
for(int i = 1; i <= n; i++) a[l[i]] = col[i];
for(int i = 1; i <= m; i++) {
int u, k;
scanf("%d%d", &u, &k);
q[i] = {l[u], r[u], i, k};
}
int unt = sqrt(n);
for(int i = 1; i <= n; i++) bl[i] = (i - 1) / unt + 1;
sort(q + 1, q + m + 1, cmp);
for(int i = 1; i <= m; i++) {
while(nowl < q[i].l) del(nowl++);
while(nowl > q[i].l) add(--nowl);
while(nowr < q[i].r) add(++nowr);
while(nowr > q[i].r) del(nowr--);
Ans[q[i].id] = sum[q[i].k];
}
for(int i = 1; i <= m; i++) printf("%d
", Ans[i]);
return 0;
}