[bzoj3611][Heoi2014]大工程

[bzoj3611][Heoi2014]大工程

标签: 虚树 DP


题目链接

题解

发现\(\sum k与n\)是同阶的,很容易想到虚树。
那么难点就在dp统计上了。
对于和的话,dp[u]表示u子树内所有边的贡献,那么$ dp[u]=dp[v]+sz[v]×(tot-sz[v])×dis(u,v) \( sz[v]就代表子树内关键点的个数,tot是树中关键点的个数。 最大值我们可以维护一个dpmx[u]代表u到子树中关键点的最长距离。 然后就用\)dpmx[u]+dpmx[v]+dis(u,v) $来更新答案。
最小值也是一样。

Code

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<set>
#include<map>
using namespace std;
#define ll long long
#define REP(i,a,b) for(int i=(a),_end_=(b);i<=_end_;i++)
#define DREP(i,a,b) for(int i=(a),_end_=(b);i>=_end_;i--)
#define EREP(i,a) for(int i=start[(a)];i;i=e[i].next)
#define EREP_g(i,a) for(int i=start_g[(a)];i;i=g[i].next)
inline int read()
{
	int sum=0,p=1;char ch=getchar();
	while(!(('0'<=ch && ch<='9') || ch=='-'))ch=getchar();
	if(ch=='-')p=-1,ch=getchar();
	while('0'<=ch && ch<='9')sum=sum*10+ch-48,ch=getchar();
	return sum*p;
}

const int maxn=1e6+20;

struct node {
	int v,next;
};

node e[maxn*2];
int cnt,start[maxn];
node g[maxn*2];
int cnt_g,start_g[maxn];

void addedge(int u,int v)
{
	e[++cnt]=(node){v,start[u]};
	start[u]=cnt;
}

void addedge_g(int u,int v)
{
	g[++cnt_g]=(node){v,start_g[u]};
	start_g[u]=cnt_g;
}

int n,deep[maxn],dfn[maxn],times,p[maxn][21];

void dfs(int u,int fa)
{
	deep[u]=deep[fa]+1;
	dfn[u]=++times;
	p[u][0]=fa;
	EREP(i,u)
	{
		int v=e[i].v;
		if(v==fa)continue;
		dfs(v,u);
	}
}

void init()
{
	n=read();
	REP(i,1,n-1)
	{
		int u=read(),v=read();
		addedge(u,v);
		addedge(v,u);
	}
	dfs(1,0);
	for(int j=1;(1<<j)<=n;j++)
		REP(i,1,n)p[i][j]=p[p[i][j-1]][j-1];
}

int lca(int u,int v)
{
	if(deep[u]<deep[v])swap(u,v);
	DREP(i,20,0)if(deep[p[u][i]]>=deep[v])u=p[u][i];
	if(u==v)return u;
	DREP(i,20,0)if(p[u][i]!=p[v][i])u=p[u][i],v=p[v][i];
	return p[u][0];
}

int tot,add[maxn],vis[maxn];
int top,st[maxn];
bool cmpx(const int a,const int b)
{
	return dfn[a]<dfn[b];
}

void Make_tree()
{
	cnt_g=0;
	tot=read();
	REP(i,1,tot)add[i]=read(),vis[add[i]]=1;
	sort(add+1,add+tot+1,cmpx);
	top=1;st[0]=0;st[1]=1;
	REP(i,1,tot)
	{
		int u=st[top],v=add[i],Lca=lca(u,v);
		if(u!=Lca)
		{
			top--;
			while(dfn[st[top]]>dfn[Lca])addedge_g(st[top],st[top+1]),addedge_g(st[top+1],st[top]),top--;
			addedge_g(Lca,st[top+1]);addedge_g(st[top+1],Lca);
			if(st[top]!=Lca)st[++top]=Lca;
		}
		if(v!=Lca)st[++top]=v;
	}
	top--;
	while(top)addedge_g(st[top],st[top+1]),addedge_g(st[top+1],st[top]),top--;
}

ll dp_s[maxn];
int dp_mx[maxn],dp_mn[maxn];
ll sz[maxn];

void dfs1(int u,int fa)
{
	if(vis[u])sz[u]=1;else sz[u]=0;
	EREP_g(i,u)
	{
		int v=g[i].v;
		if(v==fa)continue;
		dfs1(v,u);
		sz[u]+=sz[v];
	}
}

int Mx_ans,Mn_ans;

void Dp(int u,int fa)
{
	dp_s[u]=0;
	if(!vis[u])dp_mn[u]=0x3f3f3f3f;else dp_mn[u]=0;
	if(!vis[u])dp_mx[u]=-0x3f3f3f3f;else dp_mx[u]=0;
	EREP_g(i,u)
	{
		int v=g[i].v,dis=(deep[v]-deep[u]);
		if(v==fa)continue;
		Dp(v,u);
		dp_s[u]+=dp_s[v]+(tot-sz[v])*sz[v]*dis;
		Mx_ans=max(Mx_ans,dp_mx[v]+dp_mx[u]+dis);
		dp_mx[u]=max(dp_mx[u],dp_mx[v]+dis);
		Mn_ans=min(Mn_ans,dp_mn[v]+dp_mn[u]+dis);
		dp_mn[u]=min(dp_mn[u],dp_mn[v]+dis);
	}
	start_g[u]=0;vis[u]=0;
}

void doing()
{
	int q;
	q=read();
	REP(i,1,q)
	{
		Make_tree();
		dfs1(1,0);
		Mx_ans=0;Mn_ans=0x3f3f3f3f;
		Dp(1,0);
		printf("%lld %d %d\n",dp_s[1],Mn_ans,Mx_ans);
	}
}

int main()
{
	init();
	doing();
	return 0;
}


posted @ 2017-10-03 10:27  Deadecho  阅读(296)  评论(2编辑  收藏  举报