【题解】CF375D Tree and Queries

Link

\(\text{Solution:}\)

讲实话这题有点烦,不知道为啥改了下\(\text{dfs}\)就过了……原版本\(dfs\)好像没啥错啊……

其实对于子树问题,我们求出原来树的\(dfs\)序列,则可以将它转化为一个序列问题。注意题目中说的是有根树,以\(1\)为根。

那么,我们一遍\(dfs\)求出序列后,把它插到询问里面,即更新为原序列。

注意,我们对应\(dfs\)序并不是原来的点,所以还需要一个数组\(rk\)维护映射\(point\to dfn\).

那么,对于维护,一种最直接的树状数组维护前缀和,复杂度套一个\(Log\).

另一种,我们考虑一下如何\(O(1)\)来维护,维护一个计数数组和\(sum\)数组。

每次更新一个数,如果是加,那么它原来次数的\(sum\)数组不用修改,在加完的\(sum\)数组处修改即可。因为这样是一路加过去的,手动模拟一下小样例更好理解。

对于删掉一个数,显然次数\(-1\),那么对应地它当前给\(sum\)数组的贡献就不再奏效,于是先把它的贡献清除掉,再将次数\(-1\).

我们通过以上做到了\(O(1)\)维护前缀和。

那么我们就可以莫队了。将询问离线,并分块排序,复杂度\(O(n\sqrt{n}).\)

玄学\(dfs\)真不知道怎么搞的,调了半天……

#include<bits/stdc++.h>
using namespace std;
const int MAXN=5e5+10;
int dfn[MAXN],n,m,tot,head[MAXN<<1];
int cnt[MAXN],ans[MAXN],dfstime,rk[MAXN];
int bl[MAXN],S,sum[MAXN],c[MAXN],siz[MAXN];
pair<int,int>Rem[MAXN];
struct edge{
	int nxt,to;
}e[MAXN<<1];
inline void add(int x,int y){e[++tot].to=y;e[tot].nxt=head[x];head[x]=tot;}
void dfs(int x,int pre){
	rk[dfn[x]=++dfstime]=x;
	Rem[x].first=dfstime;siz[x]=1;
	for(int i=head[x];i;i=e[i].nxt){
		int j=e[i].to;
		if(pre==j)continue;
		dfs(j,x);siz[x]+=siz[j];
	}
	Rem[x].second=dfstime;
}
struct Q{
	int l,r,k,id;
	bool operator<(const Q&B)const{
		if(bl[l]==bl[B.l])return r<B.r;
		return bl[l]<bl[B.l];
	}
}q[MAXN];
inline void inr(int x){++cnt[c[rk[x]]],++sum[cnt[c[rk[x]]]];}
inline void del(int x){--sum[cnt[c[rk[x]]]],--cnt[c[rk[x]]];}

int main(){
	scanf("%d%d",&n,&m);S=sqrt(n);
	for(int i=1;i<=n;++i)scanf("%d",&c[i]),bl[i]=(i-1)/S+1;
	for(int i=1;i<n;++i){
		int x,y;
		scanf("%d%d",&x,&y);
		add(x,y);add(y,x);
	}
	dfs(1,0);
	for(int i=1,v;i<=m;++i){
		scanf("%d%d",&v,&q[i].k);q[i].id=i;
		q[i].l=dfn[v];q[i].r=dfn[v]+siz[v]-1;
	}
	sort(q+1,q+m+1);int l=1,r=0;
	for(int i=1;i<=m;++i){
		int ql=q[i].l,qr=q[i].r;
		while(l<ql)del(l++);
		while(l>ql)inr(--l);
		while(r<qr)inr(++r);
		while(r>qr)del(r--);
		ans[q[i].id]=sum[q[i].k];
	}
	for(int i=1;i<=m;++i)printf("%d\n",ans[i]);
	return 0;
}
posted @ 2020-06-04 22:24  Refined_heart  阅读(137)  评论(0编辑  收藏  举报