链接:http://acm.hdu.edu.cn/showproblem.php?pid=4358
题意:给定一棵n个节点的树和k,每个点有点权,再给q个询问,每次询问:在以v为根的子树中有多少个数恰好出现了k次。
分析:遇到子树问题,首先考虑到的就是dfs序,然后发现还有一个条件k。我的处理方式是:先离散化,然后构出dfs序,然后处理出in[],out[],lose[]数组,如果当前i中的数是某个出现次数大于等于k次的数,那么in[i]表示刚好构成k个数的初始位置,out[i]表示a[i]在下一次出现的位置的前一位(不出现了就记为n),lose[i]记录a[i]向前数第k+1个,如果i前面恰好k个a[i]那么lose[i]=0。这样我们按询问的右端点排序,每次加入i的3个值(in[i]插入1,out[i]和lose[i]插入-1),然后查询在[l,r]中有多少个1即可。我处理得有些麻烦。O(qlogn)
代码:
#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<bitset>
#include<math.h>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=100100;
const int MAX=151;
const int mod=100000000;
const int MOD1=1000000007;
const int MOD2=1000000009;
const double EPS=0.00000001;
typedef long long ll;
const ll MOD=998244353;
const ll INF=10000000010;
typedef double db;
typedef unsigned long long ull;
int tot,u[N],v[2*N],pre[2*N];
void add(int x,int y) {
v[tot]=y;pre[tot]=u[x];u[x]=tot++;
}
int k,a[N],w[N],in[N],out[N];
void dfs(int x,int y) {
k++;in[x]=k;a[k]=w[x];
for (int i=u[x];i!=-1;i=pre[i])
if (v[i]!=y) dfs(v[i],x);
out[x]=k;
}
struct node {
int l,r,id;
}qu[N];
int cmd(node x,node y) { return x.r<y.r; }
int f[N],ans[N],sub[N],fir[N],lose[N];
void add_x(int x,int y,int n) {
if (x<1) return ;
for (;x<=n+1;x+=x&(-x)) f[x]+=y;
}
int getsum(int x) {
int ret=0;
for (;x;x-=x&(-x)) ret+=f[x];
return ret;
}
int main()
{
int i,n,m,q,t,x,y,ca,R;
scanf("%d", &t);
for (ca=1;ca<=t;ca++) {
scanf("%d%d", &n, &m);
for (i=1;i<=n;i++) {
scanf("%d", &w[i]);a[i]=w[i];
}
tot=0;memset(u,-1,sizeof(u));
for (i=1;i<n;i++) {
scanf("%d%d", &x, &y);add(x,y);add(y,x);
}
sort(a+1,a+n+1);
k=unique(a+1,a+n+1)-(a+1);
for (i=1;i<=n;i++) w[i]=lower_bound(a+1,a+k+1,w[i])-a;
k=0;dfs(1,1);
scanf("%d", &q);
for (i=1;i<=q;i++) {
scanf("%d", &x);qu[i].id=i;
qu[i].l=in[x];qu[i].r=out[x];
}
sort(qu+1,qu+q+1,cmd);
memset(u,0,sizeof(u));
memset(v,0,sizeof(v));
memset(w,0,sizeof(w));
memset(sub,0,sizeof(sub));
memset(pre,0,sizeof(pre));
memset(fir,0,sizeof(fir));
memset(lose,0,sizeof(lose));
for (i=1;i<=n;i++)
if (!fir[a[i]]) fir[a[i]]=i;
for (i=1;i<=n;i++) {
pre[i]=w[a[i]];sub[w[a[i]]]=i;
w[a[i]]=i;u[a[i]]++;
if (u[a[i]]>=m) {
v[i]=1;
if (u[a[i]]==m) { in[i]=fir[a[i]];out[i]=n; }
else { out[pre[i]]=i-1;in[i]=sub[in[pre[i]]];out[i]=n;lose[i]=in[pre[i]]; }
}
}
R=0;memset(f,0,sizeof(f));
for (i=1;i<=q;i++) {
while (R<qu[i].r) {
R++;
if (v[R]) {
if (v[pre[R]]&&pre[R]) { add_x(lose[pre[R]],1,n);add_x(in[pre[R]],-1,n);add_x(out[pre[R]]+1,1,n); }
add_x(lose[R],-1,n);add_x(in[R],1,n);add_x(out[R]+1,-1,n);
}
}
ans[qu[i].id]=getsum(qu[i].r)-getsum(qu[i].l-1);
}
printf("Case #%d:\n", ca);
for (i=1;i<=q;i++) printf("%d\n", ans[i]);
if (ca!=t) printf("\n");
}
return 0;
}