zoukankan      html  css  js  c++  java
  • bzoj3572: [Hnoi2014]世界树

    传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3572

    思路:注意到m[1]+m[2]+…+m[q]<=300000

    上虚树Dp。

    先DP出虚树上每个点被哪个点管辖,记为bel[i]。

    这个从上到下更新一次答案,在从下到上更新一次答案即可。

    对于最终答案,我们遍历一遍虚树,把虚树每条边对应的点划分好即可


    然后我们考虑虚树的一条边(a,b)

    记x为既是a的儿子又是b的祖先的点,siz[a]表示a点的子树大小

    1.如果bel[a]==bel[b]那么这条边所对应的一堆实际的点也应该归bel[a]管辖

    那么f[bel[a]]+=siz[x]-siz[b]

    2.如果bel[a]!=bel[b]那么这条边(在原树上是一条路径)中一定有一个分界点mid

    使得mid及以下的点归bel[b]管辖,mid以上的点归bel[a]管辖

    倍增找出mid即可

    那么f[bel[a]]+=siz[x]-siz[mid],f[bel[b]]+=siz[mid]-siz[b]


    最后要处理的是完全没有在虚树上出现的子树,这些子树肯定被控制它们的根的点所控制

    我们记录一个rem[i]表示i的子树现在还有多少个点没被统计答案,初值为siz[i]

    处理了一条边(a,b)后,那么x的子树就在处理虚树时已经被我们处理完了,记得减去,rem[a]-=siz[x]

    最后把这些多余的点加回去即可,f[bel[a]]+=rem[a]

    然后有一个细节,记得把虚树的根的rem设为整个树的大小,而不是它的siz,因为那样会使我们漏掉原来的根的其他子树


    实现的时候注意细节(各种乱七八糟的问题)....

    不知道为什么这么慢....(看来是写丑了)


    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    const int maxn=300010,maxm=600010,maxk=22;
    using namespace std;
    int n,m,fa[maxn][maxk],dep[maxn],siz[maxn],dfn[maxn],tim,f[maxn],cnt,poi[maxn],bel[maxn],stk[maxn],top,ordc,ord[maxn],seq[maxn],rem[maxn];
    bool bo[maxn];char ch;
    bool cmp(int a,int b){return dfn[a]<dfn[b];}
    void read(int &x){
    	for (ch=getchar();!isdigit(ch);ch=getchar());
    	for (x=0;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
    }
    int lca(int a,int b){
    	if (dep[a]<dep[b]) swap(a,b);
    	for (int h=dep[a]-dep[b],i=19;i>=0;i--) if (h>=(1<<i)) h-=(1<<i),a=fa[a][i];
    	if (a==b) return a;
    	for (int i=19;i>=0;i--) if (fa[a][i]!=fa[b][i]) a=fa[a][i],b=fa[b][i];
    	return fa[a][0];
    }
    int getd(int a,int b){return dep[a]+dep[b]-(dep[lca(a,b)]<<1);}
    
    struct Tgraph{
    	int pre[maxm],now[maxn],son[maxm],tot;
    	void add(int a,int b){pre[++tot]=now[a],now[a]=tot,son[tot]=b;}
    	void dfs1(int x){
    		siz[x]=1,dfn[x]=++tim;
    		for (int i=1;i<=19;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
    		for (int y=now[x];y;y=pre[y]) if (son[y]!=fa[x][0])
    			dep[son[y]]=dep[x]+1,fa[son[y]][0]=x,dfs1(son[y]),siz[x]+=siz[son[y]];
    	}
    	void dfs2(int x){//找子树中最近的关键点
    		ord[++ordc]=x,rem[x]=siz[x];
    		for (int y=now[x];y;y=pre[y]){
    			int v=son[y];dfs2(v);
    			if (!bel[v]) continue;
    			int t1=getd(x,bel[v]),t2=getd(x,bel[x]);
    			if (!bel[x]||t1<t2||(t1==t2&&bel[v]<bel[x])) bel[x]=bel[v];
    		}
    	}
    	void dfs3(int x){//找上面最近的关键点
    		for (int y=now[x];y;y=pre[y]){
    			int v=son[y],t1=getd(v,bel[x]),t2=getd(bel[v],v);
    			if (!bel[v]||t1<t2||(t1==t2&&bel[x]<bel[v])) bel[v]=bel[x];
    			dfs3(v);
    		}
    	}
    	void solve(int a,int b){//处理虚边a->b
    		int x=b,mid=b;
    		for (int i=19;i>=0;i--) if (dep[fa[x][i]]>dep[a]) x=fa[x][i];
    		rem[a]-=siz[x];
    		if (bel[a]==bel[b]){f[bel[a]]+=siz[x]-siz[b];return;}
    		for (int i=19;i>=0;i--){
    			int nxt=fa[mid][i];
    			if (dep[nxt]<=dep[a]) continue;
    			int t1=getd(bel[a],nxt),t2=getd(bel[b],nxt);
    			if (t2<t1||(t1==t2&&bel[b]<bel[a])) mid=nxt;
    		}
    		f[bel[a]]+=siz[x]-siz[mid],f[bel[b]]+=siz[mid]-siz[b];
    	}
    	void getans(){
    		for (int i=1;i<=ordc;i++) for (int y=now[ord[i]];y;y=pre[y]) solve(ord[i],son[y]);
    		for (int i=1;i<=ordc;i++) f[bel[ord[i]]]+=rem[ord[i]];
    		for (int i=1;i<=cnt;i++) printf("%d ",f[seq[i]]);puts("");
    	}
    }g1,g2;
    
    void work(){
    	stk[top=1]=poi[1];
    	for (int i=2;i<=cnt;i++){
    		int u=lca(poi[i],stk[top]);
    		while (dfn[stk[top]]>dfn[u]){
    			if (dfn[stk[top-1]]<=dfn[u]){
    				g2.add(u,stk[top]);
    				if (u!=stk[--top]) stk[++top]=u;
    				break;
    			}
    			g2.add(stk[top-1],stk[top]),top--;
    		}
    		stk[++top]=poi[i];
    	}
    	while (top>1) g2.add(stk[top-1],stk[top]),top--;
    	g2.dfs2(stk[1]),g2.dfs3(stk[1]),rem[stk[1]]=siz[1],g2.getans();
    	for (int i=1;i<=ordc;i++){int p=ord[i];f[p]=g2.now[p]=rem[p]=bel[p]=0;}
    	g2.tot=ordc=0;
    }
    
    int main(){
    	read(n);
    	for (int i=1,a,b;i<n;i++) read(a),read(b),g1.add(a,b),g1.add(b,a);
    	g1.dfs1(1),read(m);
    	while (m--){
    		read(cnt);
    		for (int i=1;i<=cnt;i++) read(poi[i]),seq[i]=poi[i],bo[poi[i]]=1,bel[poi[i]]=poi[i];
    		sort(poi+1,poi+1+cnt,cmp),work();
    	}
    	return 0;
    }
    
    /*
    10
    2 1
    3 2
    4 3
    5 4
    6 1
    7 3
    8 3
    9 4
    10 1
    5
    2
    6 1
    5
    2 7 3 6 9
    1
    8
    4
    8 7 10 3
    5
    2 9 3 5 8
    */


  • 相关阅读:
    thread_Semaphore信号量
    c 语言文本文件判断是否到达结尾的问题
    c语言快速排序算法(转)
    c语言双向循环链表
    gtk+学习笔记(八)
    c语言循环链表的问题
    linux c获取本地时间
    gtk+学习笔记(七)
    gtk+学习笔记(六)
    gtk+学习笔记(五)
  • 原文地址:https://www.cnblogs.com/thythy/p/5493491.html
Copyright © 2011-2022 走看看