• [HEOI2014]大工程


    III.[HEOI2014]大工程

    仍然建出虚树。

    我们考虑设\(sz_x\)表示\(x\)子树中实点(即原本点集中的点)的数量,再设\(f_x\)表示\(x\)\(x\)子树中某个实点的最长路径,\(g_x\)则表示最短路径。

    我们先考虑求\(\min\)\(\max\)的部分。

    对于一个实点,它初始值\(f_x=g_x=0\)(有一条到自己的路径);对于一条虚点,它初始\(f_x=-\infty,g_x=\infty\)

    我们枚举它的儿子\(y\)。设\(z\)表示这条边的边权(实际上就是\(y\)的深度减去\(x\)的深度),则我们可以用\(f_x+z+f_y\)拼成一条最长路径,\(g_x+z+g_y\)拼成一条最短路径。然后更新\(f\)\(g\)即可,就跟正常树中求直径的做法没啥区别。

    我们再考虑求和的地方。

    显然,对于一条边\((x,y)\)(这里令\(x\)\(y\)在虚树上的父亲),它总共会被经过\(sz_y\times(q-sz_y)\)次,其中\(q\)是点集大小。这也很好理解——对于\(y\)侧树中每个实点,它都会与\(x\)侧树中每个实点有一条边。则只需要用次数乘上边权,再求和,便是答案。

    这题我担心\(n\log n\)空间的倍增或者ST表可能会MLE,就使用了树剖。但是总复杂度仍然是\(O(n\log n)\)的。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int n,m,fa[1001000],dep[1001000],dfn[1001000],top[1001000];
    namespace real{
    	int sz[1001000],son[1001000],tot;
    	vector<int>v[1001000];
    	void dfs1(int x,int Fa){
    		fa[x]=Fa,dep[x]=dep[Fa]+1,dfn[x]=++tot,sz[x]=1;
    		for(auto y:v[x]){
    			if(y==fa[x])continue;
    			dfs1(y,x),sz[x]+=sz[y];
    			if(sz[son[x]]<sz[y])son[x]=y;
    		}
    	}
    	void dfs2(int x){
    		if(son[x])top[son[x]]=top[x],dfs2(son[x]);
    		for(auto y:v[x])if(y!=fa[x]&&y!=son[x])top[y]=y,dfs2(y);
    	}
    }
    int LCA(int x,int y){
    	while(top[x]!=top[y])dep[top[x]]>dep[top[y]]?x=fa[top[x]]:y=fa[top[y]];
    	return dep[x]<dep[y]?x:y;
    }
    namespace imag{
    	int q,stk[1001000],tp,a[1001000],sz[1001000],mn,mx,f[1001000],g[1001000];
    	ll res;
    	vector<int>v[1001000];
    	bool cmp(int x,int y){
    		return dfn[x]<dfn[y];
    	}
    	void ins(int x){
    		sz[x]=1;
    		if(!tp){stk[++tp]=x;return;}
    		int lca=LCA(x,stk[tp]);
    		while(tp>=2&&dep[lca]<dep[stk[tp-1]])v[stk[tp-1]].push_back(stk[tp]),tp--;
    		if(tp&&dep[lca]<dep[stk[tp]])v[lca].push_back(stk[tp--]);
    		if(!tp||stk[tp]!=lca)stk[++tp]=lca;
    		stk[++tp]=x;
    	}
    	void fin(){
    		while(tp>=2)v[stk[tp-1]].push_back(stk[tp]),tp--;
    		tp--;
    	}
    	void dfs0(int x){
    		if(sz[x])f[x]=g[x]=0;
    		else f[x]=-0x3f3f3f3f,g[x]=0x3f3f3f3f;
    		for(auto y:v[x]){
    			dfs0(y);
    			mx=max(mx,f[x]+(dep[y]-dep[x])+f[y]),f[x]=max(f[x],(dep[y]-dep[x])+f[y]);
    			mn=min(mn,g[x]+(dep[y]-dep[x])+g[y]),g[x]=min(g[x],(dep[y]-dep[x])+g[y]);
    		}
    	}
    	void dfs1(int x){
    		for(auto y:v[x])dfs1(y),res+=1ll*(dep[y]-dep[x])*sz[y]*(q-sz[y]),sz[x]+=sz[y];
    	}
    	void dfs2(int x){
    		sz[x]=0;
    		for(auto y:v[x])dfs2(y);
    		v[x].clear();
    	}
    	void work(){
    		res=0,mn=0x3f3f3f3f,mx=-0x3f3f3f3f,scanf("%d",&q);
    		for(int i=1;i<=q;i++)scanf("%d",&a[i]);
    		sort(a+1,a+q+1,cmp);
    		if(a[1]!=1)stk[++tp]=1;
    		for(int i=1;i<=q;i++)ins(a[i]); 
    		fin();
    		dfs0(1);
    		dfs1(1);
    		printf("%lld %lld %lld\n",res,mn,mx);
    		dfs2(1);
    	}
    }
    int main(){
    	scanf("%d",&n);
    	for(int i=1,x,y;i<n;i++)scanf("%d%d",&x,&y),real::v[x].push_back(y),real::v[y].push_back(x);
    	real::dfs1(1,0),top[1]=1,real::dfs2(1);
    	scanf("%d",&m);
    	while(m--)imag::work();
    	return 0;
    }
    

  • 相关阅读:
    python-TCP传输模型
    python-锁机制
    python-生产者消费者模式
    python-Lock锁线程同步和互斥
    python-Event事件线程同步和互斥
    python-thread封装类创建线程
    python-fifo管道文件通信
    python-thread多线程
    Sublime一些设置
    gdb的user-define command
  • 原文地址:https://www.cnblogs.com/Troverld/p/14621454.html
Copyright © 2020-2023  润新知