题目链接:Click here~~
题意:
给一颗 n 个节点的树,每个节点有权值 wi,然后 q 次询问,每次询问根为 u 的子树有多少个 wi 恰好出现了 k 次。
解题思路:
又是对 子树 的 操作/询问,而且满足区间性质,所以可以先将每棵子树转化成相应的区间。
于是问题变成,每次询问一段区间中有多少个数恰好出现了 k 次。
做法是将询问离线搞,离线只做过一两道,丝毫没功底,TAT。
先将所有询问按右端点排序,然后维护一个点值的集合,点值 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; }