[虚树][树形dp] Bzoj P3611 大工程

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就好了

代码

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstdlib>
 4 #include<cstring>
 5 #include<algorithm>
 6 #include<cmath>
 7 #define N 1000010
 8 #define ll long long
 9 #define inf 0x3fffffff
10 using namespace std;
11 int cnt,n,m,k,head[N],last[N],dep[N],fa[N][20],size[N],mx[N],mn[N],ans2,ans3,tim,dfn[N],logn,a[N],flag[N],st[N];
12 ll sum[N],ans1;
13 struct edge{int to,from;}e[N*2];
14 void insert1(int u,int v)
15 {
16     e[++cnt].to=v,e[cnt].from=head[u],head[u]=cnt;
17     e[++cnt].to=u,e[cnt].from=head[v],head[v]=cnt;
18 }
19 void insert2(int u,int v)
20 {
21     if (u==v) return;
22     e[++cnt].to=v,e[cnt].from=last[u],last[u]=cnt;
23 }
24 void dfs(int x)
25 {
26     dfn[x]=++tim;
27     for (int i=1;i<=logn;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
28     dep[x]=dep[fa[x][0]]+1;
29     for (int i=head[x];i;i=e[i].from) if (e[i].to!=fa[x][0]) fa[e[i].to][0]=x,dfs(e[i].to);
30 }
31 int getlca(int x,int y)
32 {
33     if (dep[x]<dep[y]) swap(x,y);
34     for (int i=logn;i>=0;i--) if (dep[fa[x][i]]>=dep[y]) x=fa[x][i];
35     if (x==y) return x;
36     for (int i=logn;i>=0;i--) if (fa[x][0]!=fa[y][0]) x=fa[x][0],y=fa[y][0];
37     return fa[x][0];
38 }
39 bool cmp(int a,int b) { return dfn[a]<dfn[b]; }
40 void dp(int x)
41 {
42     int mx1=0,mx2=0,mn1=inf,mn2=inf;size[x]=0;sum[x]=0;
43     if (flag[x]) size[x]++,sum[x]=dep[x],mn1=0;
44     for (int i=last[x];i;i=e[i].from)
45     {
46         dp(e[i].to);
47         size[x]+=size[e[i].to];sum[x]+=sum[e[i].to];
48         if (mx[e[i].to]+dep[e[i].to]-dep[x]>mx1) mx2=mx1,mx1=mx[e[i].to]+dep[e[i].to]-dep[x];
49         else if (mx[e[i].to]+dep[e[i].to]-dep[x]>mx2) mx2=mx[e[i].to]+dep[e[i].to]-dep[x];
50         if (mn[e[i].to]+dep[e[i].to]-dep[x]<mn1) mn2=mn1,mn1=mn[e[i].to]+dep[e[i].to]-dep[x];
51         else if (mn[e[i].to]+dep[e[i].to]-dep[x]<mn2) mn2=mn[e[i].to]+dep[e[i].to]-dep[x];
52     }
53     for (int i=last[x];i;i=e[i].from) ans1+=(ll)(size[x]-size[e[i].to])*(sum[e[i].to]-(ll)size[e[i].to]*dep[x]);
54     ans2=min(ans2,mn1+mn2);
55     if (flag[x]||!flag[x]&&mx2) ans3=max(ans3,mx1+mx2);
56     mx[x]=mx1;mn[x]=mn1,last[x]=0;
57 }
58 void solve()
59 {
60     cnt=0,scanf("%d",&k);
61     for (int i=1;i<=k;i++) scanf("%d",&a[i]),flag[a[i]]=1;
62     sort(a+1,a+k+1,cmp);
63     int top=1; st[1]=1;
64     for (int i=1;i<=k;i++)
65     {
66         int now=a[i],f=getlca(now,st[top]);
67         while (1)
68         {
69             if (dep[f]>=dep[st[top-1]])
70             {
71                 insert2(f,st[top]),top--;
72                 if (st[top]!=f) st[++top]=f;
73                 break;
74             }
75             insert2(st[top-1],st[top]),top--;
76         }
77         if (st[top]!=now) st[++top]=now;
78     }
79     while (top>1) insert2(st[top-1],st[top]),top--;
80     ans1=ans3=0;ans2=inf,dp(1);
81     for (int i=1;i<=k;i++) flag[a[i]]=0;
82     printf("%lld %d %d\n",ans1,ans2,ans3);
83 }
84 int main()
85 {
86     freopen("data.in","r",stdin);
87     scanf("%d",&n);
88     for (int i=1,x,y;i<n;i++) scanf("%d%d",&x,&y),insert1(x,y);
89     logn=log(n)/log(2),dfs(1),scanf("%d",&m);
90     for (int i=1;i<=m;i++) solve();
91 }

 

posted @ 2019-07-05 10:28  BEYang_Z  阅读(146)  评论(0编辑  收藏  举报