Boring counting
Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 98304/98304 K (Java/Others)Total Submission(s): 1584 Accepted Submission(s): 440
Problem Description
In this problem we consider a rooted tree with N vertices. The vertices are numbered from 1 to N, and vertex 1 represents the root. There are integer weights on each vectice. Your task is to answer a list of queries, for each query, please tell us among all the vertices in the subtree rooted at vertice u, how many different kinds of weights appear exactly K times?
Input
The first line of the input contains an integer T( T<= 5 ), indicating the number of test cases.
For each test case, the first line contains two integers N and K, as described above. ( 1<= N <= 10 5, 1 <= K <= N )
Then come N integers in the second line, they are the weights of vertice 1 to N. ( 0 <= weight <= 10 9 )
For next N-1 lines, each line contains two vertices u and v, which is connected in the tree.
Next line is a integer Q, representing the number of queries. (1 <= Q <= 10 5)
For next Q lines, each with an integer u, as the root of the subtree described above.
For each test case, the first line contains two integers N and K, as described above. ( 1<= N <= 10 5, 1 <= K <= N )
Then come N integers in the second line, they are the weights of vertice 1 to N. ( 0 <= weight <= 10 9 )
For next N-1 lines, each line contains two vertices u and v, which is connected in the tree.
Next line is a integer Q, representing the number of queries. (1 <= Q <= 10 5)
For next Q lines, each with an integer u, as the root of the subtree described above.
Output
For each test case, output "Case #X:" first, X is the test number. Then output Q lines, each with a number -- the answer to each query.
Seperate each test case with an empty line.
Seperate each test case with an empty line.
Sample Input
1 3 1 1 2 2 1 2 1 3 3 2 1 3
Sample Output
Case #1: 1 1 1
Author
fish@UESTC_Oblivion
Source
Recommend
zhuyuanchen520
思路:将一棵树转化成区间,可以用时间戳,查询一个节点就可以表示成一个区间。然后这题就可以变成求一个区间内出现过K次的数字的个数。这里可以树状数组来维护一段"时间区间"中出现k次的数字的个数。利用离线,对每次查询的区间按右区间从小到大排序。按时间遍历,每次将当前时间的u值的位置插入到pos[u]的容器。之后每次更新的时候,都要将之前的区间删除,所以需要维护u值出现的位置。sum(q[i].l)就是当前查询的结果。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;
const int maxn=100005;
int c[maxn],index,n;
int head[maxn],edge,begin[maxn],end[maxn];
int w[maxn],ans[maxn],value[maxn];
int to[maxn<<1],next[maxn<<1];
bool vis[maxn];
vector<int>pos[maxn];
map<int,int>wflag;
struct node
{
int l,r,id;
} q[maxn];
bool cmp(node a,node b)
{
return a.r<b.r;
}
inline void addedge(int u,int v)
{
to[edge]=v,next[edge]=head[u],head[u]=edge++;
}
void init()
{
wflag.clear();
memset(c,0,sizeof(c));
memset(head,-1,sizeof(head));
memset(vis,0,sizeof(vis));
edge=index=0;
}
inline int lowbit(int x)
{
return x&(-x);
}
void add(int x,int val)
{
for(;x<=n;x+=lowbit(x))
c[x]+=val;
}
int sum(int x)
{
int s=0;
while(x>0)
s+=c[x],x-=lowbit(x);
return s;
}
void dfs(int now)
{
begin[now]=++index;
value[index]=w[now];
vis[now]=1;
for(int i=head[now]; ~i; i=next[i])
if(!vis[to[i]])
dfs(to[i]);
end[now]=index;
}
int main()
{
int u,v,m,t,k;
scanf("%d",&t);
for(int ca=1; ca<=t; ca++)
{
scanf("%d%d",&n,&k);
init();
int cnt=0;
for(int i=1; i<=n; i++)
{
scanf("%d",&w[i]);
if(wflag[w[i]]==0)
{
wflag[w[i]]=++cnt;
w[i]=cnt;
}
else w[i]=wflag[w[i]];
}
for(int i=0; i<=cnt; i++)
{
pos[i].clear();
pos[i].push_back(0);
}
for(int i=0; i<n-1; i++)
{
scanf("%d%d",&u,&v);
addedge(u,v);
addedge(v,u);
}
dfs(1);
scanf("%d",&m);
for(int i=0; i<m; i++)
{
scanf("%d",&u);
q[i].l=begin[u];
q[i].r=end[u];
q[i].id=i;
}
sort(q,q+m,cmp);
cnt=0;
for(int i=1; i<=n; i++)
{
u=value[i];
pos[u].push_back(i);
int tmp=pos[u].size()-1;
if(tmp>=k)
{
if(tmp>k)//消除上一次含K个U值的区间更新
{
add(pos[u][tmp-k-1]+1,-1);
add(pos[u][tmp-k]+1,1);
}
//更新这一次含有K个U值的区间
add(pos[u][tmp-k]+1,1);
add(pos[u][tmp-k+1]+1,-1);
}
while(q[cnt].r==i)
{
ans[q[cnt].id]=sum(q[cnt].l);
cnt++;
}
}
if(ca>1) puts("");
printf("Case #%d:\n",ca);
for(int i=0; i<m; i++)
printf("%d\n",ans[i]);
}
return 0;
}