CF375D Tree and Queries
题目大意:给出一棵 n 个结点的树,每个结点有一个颜色 c[i]。 询问 q 次,每次询问以 v 结点为根的子树中,出现次数 ≥k 的颜色有多少种。树的根节点是1。 (1<=c[i],n,m<=100000)。
思路:这是一道树上问题转成序列问题的题目,我们先利用dfs序将树上问题变成序列问题,然后我们的问题就变成了:在1个序列内,询问q次,每次询问[l,r]中颜色出现次数大于等于k的颜色有多少种。我们可以用莫队来维护他,将每个l 按照所处块为第一关键字,右端点为第二关键字排序,然后开个桶记录颜色出现次数,再开个ans[i]记录颜色等于i的数量(当然也可以用树状数组维护,复杂度多个log,应该能过),因为莫队每次移动只会使ans数组+1或-1,所以大于等于i的颜色必定会经过ans[i],那么最后的答案也就是ans[i];复杂度O(n·log(n))。
代码如下:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> using namespace std; const int MaxN=200000; struct edge{ int to,next; }e[MaxN*2]; struct node{ int bel,l,r,id,k; }M[MaxN]; int n,m,las,tto,a[MaxN],b[MaxN],last[MaxN],tot[MaxN],pd[MaxN]; int id,dfn[MaxN],bel[MaxN],siz[MaxN],numb[MaxN],ans[MaxN]; void add_edge(int x,int y){ e[++tto].to=y;e[tto].next=last[x];last[x]=tto; return; } void dfs(int x,int f){ siz[x]=1;dfn[x]=++id;bel[id]=x; for(int i=last[x];i;i=e[i].next){ int u=e[i].to; if(u==f) continue; dfs(u,x);siz[x]+=siz[u]; } return; } int cmp(node a,node b){ return a.bel==b.bel?a.r<b.r:a.bel<b.bel;//! } int main(){ int x,y,v,k; scanf("%d%d",&n,&m);int s=sqrt(n); for(int i=1;i<=n;++i) scanf("%d",&a[i]); for(int i=1;i<n;++i) scanf("%d%d",&x,&y),add_edge(x,y),add_edge(y,x); dfs(1,0);for(int i=1;i<=n;++i) b[i]=a[bel[i]]; for(int i=1;i<=m;++i){ scanf("%d%d",&v,&k); M[i].bel=dfn[v]/s;M[i].l=dfn[v];M[i].r=dfn[v]+siz[v]-1;M[i].id=i;M[i].k=k; } sort(M+1,M+m+1,cmp); int ll=M[1].l,lr=M[1].r; for(int i=ll;i<=lr;++i){ tot[b[i]]++;numb[tot[b[i]]]++; } ans[M[1].id]=numb[M[1].k]; for(int i=2;i<=m;++i){ int nl=M[i].l,nr=M[i].r; while(ll<nl) numb[tot[b[ll]]]--,tot[b[ll]]--,++ll; while(ll>nl) tot[b[--ll]]++,numb[tot[b[ll]]]++; while(lr<nr) tot[b[++lr]]++,numb[tot[b[lr]]]++; while(lr>nr) numb[tot[b[lr]]]--,tot[b[lr]]--,--lr; ans[M[i].id]=numb[M[i].k]; } for(int i=1;i<=m;++i) printf("%d\n",ans[i]); return 0; }
题外话:这是我比赛时遇到的题,当时树上莫队打出来了,结果排序时以左端点为第二关键字被卡常了90分qwq