【BZOJ3611】大工程(虚树,动态规划)
【BZOJ3611】大工程(虚树,动态规划)
题面
Description
国家有一个大工程,要给一个非常大的交通网络里建一些新的通道。
我们这个国家位置非常特殊,可以看成是一个单位边权的树,城市位于顶点上。
在 2 个国家 a,b 之间建一条新通道需要的代价为树上 a,b 的最短路径。
现在国家有很多个计划,每个计划都是这样,我们选中了 k 个点,然后在它们两两之间 新建 C(k,2)条 新通道。
现在对于每个计划,我们想知道:
1.这些新通道的代价和
2.这些新通道中代价最小的是多少
3.这些新通道中代价最大的是多少
Input
第一行 n 表示点数。
接下来 n-1 行,每行两个数 a,b 表示 a 和 b 之间有一条边。
点从 1 开始标号。 接下来一行 q 表示计划数。
对每个计划有 2 行,第一行 k 表示这个计划选中了几个点。
第二行用空格隔开的 k 个互不相同的数表示选了哪 k 个点。
Output
输出 q 行,每行三个数分别表示代价和,最小代价,最大代价。
Sample Input
10
2 1
3 2
4 1
5 2
6 4
7 5
8 6
9 7
10 9
5
2
5 4
2
10 4
2
5 2
2
6 1
2
6 1
Sample Output
3 3 3
6 6 6
1 1 1
2 2 2
2 2 2
HINT
n<=1000000
q<=50000并且保证所有k之和<=2*n
题解
先考虑正常的\(dp\)
对于第一个,总和。设\(f[i]\)表示\(i\)的子树中的关键点的个数
转移:\(sum+=f[v]*(K-f[v])*len(i,v),f[i]+=f[v]\)
对于第二个和第三个,相当于维护树上最长链和最短链
这个就非常基础了,不写了。
现在再把这个\(dp\)放在虚树上做就行了
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 1001000
inline int read()
{
RG int x=0,t=1;RG char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=-1,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*t;
}
struct Line{int v,next,w;}e[MAX<<1];
int h[MAX],cnt=1;
inline void Add(int u,int v,int w){e[cnt]=(Line){v,h[u],w};h[u]=cnt++;}
int n,Q,K;
int fa[MAX],size[MAX],hson[MAX],dep[MAX],top[MAX],dfn[MAX],low[MAX],tim;
void dfs1(int u,int ff)
{
fa[u]=ff;dep[u]=dep[ff]+1;size[u]=1;
for(int i=h[u];i;i=e[i].next)
{
int v=e[i].v;if(v==ff)continue;
dfs1(v,u);size[u]+=size[v];
if(size[v]>size[hson[u]])hson[u]=v;
}
}
void dfs2(int u,int tp)
{
top[u]=tp;dfn[u]=++tim;
if(hson[u])dfs2(hson[u],tp);
for(int i=h[u];i;i=e[i].next)
{
int v=e[i].v;
if(v==hson[u]||v==fa[u])continue;
dfs2(v,v);
}
low[u]=tim;
}
int LCA(int u,int v)
{
while(top[u]^top[v])(dep[top[u]]<dep[top[v]])?v=fa[top[v]]:u=fa[top[u]];
return dep[u]<dep[v]?u:v;
}
int S[MAX],p[MAX<<1];
bool vis[MAX];
bool cmp(int u,int v){return dfn[u]<dfn[v];}
int Min,Max,KK;
ll Sum;
int f1[MAX],f2[MAX],f3[MAX];
void DP(int u)
{
if(vis[u])f1[u]=1,f2[u]=0,f3[u]=0;
else f1[u]=0,f2[u]=1e9,f3[u]=-1e9;
for(int i=h[u];i;i=e[i].next)
{
int v=e[i].v;DP(v);
Sum+=1ll*f1[v]*(KK-f1[v])*e[i].w;f1[u]+=f1[v];
Min=min(Min,f2[u]+f2[v]+e[i].w);f2[u]=min(f2[u],f2[v]+e[i].w);
Max=max(Max,f3[u]+f3[v]+e[i].w);f3[u]=max(f3[u],f3[v]+e[i].w);
}
}
int main()
{
n=read();
for(int i=1;i<n;++i)
{
int u=read(),v=read();
Add(u,v,0);Add(v,u,0);
}
dfs1(1,0);dfs2(1,1);
memset(h,0,sizeof(h));
Q=read();
while(Q--)
{
K=KK=read();cnt=1;int tp=0;
for(int i=1;i<=K;++i)vis[p[i]=read()]=true;
sort(&p[1],&p[K+1],cmp);
for(int i=K;i>1;--i)p[++K]=LCA(p[i],p[i-1]);
sort(&p[1],&p[K+1],cmp);K=unique(&p[1],&p[K+1])-p-1;
for(int i=1;i<=K;++i)
{
while(tp&&low[S[tp]]<dfn[p[i]])--tp;
Add(S[tp],p[i],dep[p[i]]-dep[S[tp]]);
S[++tp]=p[i];
}
Sum=0;Min=1e9;Max=-1e9;DP(p[1]);
printf("%lld %d %d\n",Sum,Min,Max);
for(int i=1;i<=K;++i)vis[p[i]]=false,h[p[i]]=0;
}
return 0;
}