[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;
}