先dfs一下把树转换成对应的数组,对每一个询问就相当于求每个区间内出现k次的不同数的个数。然后类似HDOJ3874就可以用树状数组来维护了,运用离线的思想,把询问的区间按右端点排序,再预处理出每个数出现的前一次的位置pre和前k-1次的位置pk。
pre[pre[pk[i]]]<------1---->pre[pk[i]]<----2------>pk[i]<----3------->i
对于i号位置上的数,如果pk[i]存在,则对于右端点是位置i的询问来说如果左端点在2段内,则i位置上的数至少出现了k次(对应树状数组更新(pre[pk[i]]+1,+1) , (pk[i]+1,-1) )。如果在pk之前还有这个数还出现过,也就是说如果询问的左端点落在了1段内,那么这个数出现的次数就大于k了,这个数就不能算了(对应树状数组更新(pre[pre[pk[i]]]+1,-1) , (pre[pk[i]+1,+1)),所以要把数组给更新回来。。。。这样就只要SUM(询问左端点)就是答案。
因为数的大小没有意义,为了方便可以先离散化一下。
『网上好多代码都是错的,可以去Discuss里看一下那组样例』
1 3 1 1 2 2 1 2 1 3 3 2 1 3
Case #1: 1 1 1
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #pragma comment(linker, "/STACK:102400000,102400000") using namespace std; const int maxn=100100; int n,Q,K,val[maxn]; /****************数状数组***********************/ int tree[maxn]; int lowbit(int x) { return x&(-x); } void ADD(int p,int v) { for(int i=p;i<=n;i+=lowbit(i)) tree[i]+=v; } int SUM(int p) { int sum=0; for(int i=p;i;i-=lowbit(i)) sum+=tree[i]; return sum; } /****************前向星*************************/ struct Edge { int to,next; }edge[maxn*2]; int Adj[maxn],Size; void build_init() { Size=0; memset(Adj,-1,sizeof(Adj)); } void Add_Edge(int u,int v) { edge[Size].to=v; edge[Size].next=Adj[u]; Adj[u]=Size++; } /*****************dfs树转数组************************/ int time=1; int ax[maxn]; struct Interval { int l,r; }I[maxn]; void dfs(int x,int fa) { I[x].l=time; for(int i=Adj[x];~i;i=edge[i].next) { int v=edge[i].to; if(v==fa) continue; dfs(v,x); time++; } ax[time]=val[x]; I[x].r=time; } /*******************Lisan*****************************/ int r[maxn],md[maxn],m; bool cmpR(int a,int b) { return val[a]<val[b]; } void Lisan() { for(int i=0;i<=n;i++) r[i]=i; sort(r+1,r+1+n,cmpR); md[1]=val[r[1]]; val[r[1]]=m=1; for(int i=2;i<=n;i++) { if(md[m]!=val[r[i]]) md[++m]=val[r[i]]; val[r[i]]=m; } } /********************others**************************/ struct ASK { int l,r,id; }ask[maxn]; int ans[maxn]; bool cmp(ASK a,ASK b) { if(a.r!=b.r) return a.r<b.r; return a.l<b.l; } int pre[maxn],pk[maxn],idx[maxn]; void getPRE() { memset(idx,0,sizeof(idx)); memset(pre,0,sizeof(pre)); for(int i=1;i<=n;i++) { pre[i]=idx[val[i]]; idx[val[i]]=i; } } void getPK() { memset(idx,-1,sizeof(idx)); memset(pk,0,sizeof(pk)); for(int i=n;i>=1;i--) { if(idx[val[i]]==-1) { int pos=i,cur=K-1; while(pos&&cur) { cur--; pos=pre[pos]; } pk[i]=pos; idx[val[i]]=pk[i]; } else { pk[i]=pre[idx[val[i]]]; idx[val[i]]=pk[i]; } } } int main() { int T,cas=1; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&K); memset(val,0,sizeof(val)); for(int i=1;i<=n;i++) scanf("%d",&val[i]); build_init(); for(int i=0;i<n-1;i++) { int u,v; scanf("%d%d",&u,&v); Add_Edge(u,v); Add_Edge(v,u); } time=1; dfs(1,-1); memcpy(val,ax,sizeof(ax)); Lisan(); getPRE(); getPK(); scanf("%d",&Q); for(int i=0;i<Q;i++) { int xx; scanf("%d",&xx); ask[i].l=I[xx].l,ask[i].r=I[xx].r,ask[i].id=i; } sort(ask,ask+Q,cmp); memset(tree,0,sizeof(tree)); memset(ans,0,sizeof(ans)); int pnt=0; for(int i=1;i<=n;i++) { if(pk[i]) { int x=pre[pk[i]]; if(x) { ADD(pre[x]+1,-1); ADD(x+1,1); } ADD(x+1,1); ADD(pk[i]+1,-1); } while(pnt<Q&&ask[pnt].r==i) { ans[ask[pnt].id]=SUM(ask[pnt].l); pnt++; } } printf("Case #%d:\n",cas++); for(int i=0;i<Q;i++) printf("%d\n",ans[i]); if(T) putchar(10); } return 0; }