zoukankan      html  css  js  c++  java
  • 【题解】CF375D Tree and Queries

    Link

    ( ext{Solution:})

    讲实话这题有点烦,不知道为啥改了下$ ext$就过了……原版本$dfs$好像没啥错啊……

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

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

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

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

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

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

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

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

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

    玄学$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
    ",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    搭建armlinuxgcc交叉编译工具链环境(Android原生(JNI)开发环境搭建)
    linux vi命令详解
    Android手机在开发调试时logcat不显示输出信息的解决办法
    2012的总结和13的展望
    Gvim编码学习笔记
    vue自定义过滤器格式化时间为年、月、日、小时、分钟、刚刚 J
    学校网站群建设理念
    何为真正网站群?
    手机网站——移动互联网新趋势
    建站是浮云,We7很给力
  • 原文地址:https://www.cnblogs.com/h-lka/p/13046903.html
Copyright © 2011-2022 走看看