题目链接:Click here~~
题意:
给一颗 n 个节点的树,每个节点有权值 wi,然后 q 次询问,每次询问根为 u 的子树有多少个 wi 恰好出现了 k 次。
解题思路:
又是对 子树 的 操作/询问,而且满足区间性质,所以可以先将每棵子树转化成相应的区间。
于是问题变成,每次询问一段区间中有多少个数恰好出现了 k 次。
先将所有询问按右端点排序,然后维护一个点值的集合,点值 j 表示的是区间 [j,i] 中恰好出现 k 次的数的个数。
考虑如何维护答案。为了方便起见,用 pos[v][m] 表示 v 第 m 次出现的位置。
对于新加入位置 i 的值 v,设这是 v 的第 p 次出现,这个事件只会使区间 [ pos[v][p-k]+1 , pos[v][p-k+1] ] 的结果加 1,使区间 [ pos[v][p-k-1]+1 , pos[v][p-k] ] 的结果减 1。
对于位置的维护可以先把每个值用 map 离散化成 [1,n] 以内,再用 vector[ ] 记录。点值的维护用树状数组可以搞定。
#pragma comment(linker,"/STACK:102400000,102400000")
#include <map>
#include <vector>
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int N = 1e5 + 5;
vector<int> g[N],pos[N];
map<int,int> M;
int a[N],c[N],hash[N],ans[N],id;
pair<int,int> interval[N];
struct QAQ
{
int l,r,id;
QAQ(){}
QAQ(int l,int r,int i):l(l),r(r),id(i){}
bool operator < (const QAQ& S) const{
return r < S.r;
}
}query[N];
void dfs(int pre,int u)
{
interval[u].first = ++id;
hash[id] = u;
for(int i=0;i<(int)g[u].size();i++)
{
int v = g[u][i];
if(v == pre)
continue;
dfs(u,v);
}
interval[u].second = id;
}
inline int lowbit(int x){
return x & -x;
}
void add(int loc,int val){
while(loc < N){
c[loc] += val;
loc += lowbit(loc);
}
}
void add(int a,int b,int val){
add(a,val);
add(b+1,-val);
}
int sum(int loc){
int ret = 0;
while(loc){
ret += c[loc];
loc -= lowbit(loc);
}
return ret;
}
int main()
{
int T,n,k,Q, ncase = 0;
scanf("%d",&T);
while(T--)
{
M.clear();
memset(c,0,sizeof(c));
scanf("%d%d",&n,&k);
id = 0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(!M.count(a[i]))
{
M[a[i]] = ++id;
a[i] = id;
pos[id].clear();
pos[id].push_back(0);
}
else
a[i] = M[ a[i] ];
g[i].clear();
}
for(int i=0;i<n-1;i++)
{
int u,v;
scanf("%d%d",&u,&v);
g[u].push_back(v);
g[v].push_back(u);
}
id = 0;
dfs(0,1);
scanf("%d",&Q);
for(int i=0;i<Q;i++)
{
int u;
scanf("%d",&u);
query[i] = QAQ(interval[u].first,interval[u].second,i);
}
sort(query,query+Q);
int j = 0;
for(int i=1;i<=n;i++)
{
int v = a[ hash[i] ];
pos[v].push_back(i);
int p = (int)pos[v].size() - 1;
if(p >= k)
{
add(pos[v][p-k]+1,pos[v][p-k+1],1);
if(p > k)
add(pos[v][p-k-1]+1,pos[v][p-k],-1);
}
while(j < Q && query[j].r == i)
{
ans[ query[j].id ] = sum(query[j].l);
j++;
}
}
if(ncase)
puts("");
printf("Case #%d:\n",++ncase);
for(int i=0;i<Q;i++)
printf("%d\n",ans[i]);
}
return 0;
}