[Heoi2014]大工程
3611: [Heoi2014]大工程
Time Limit: 60 Sec Memory Limit: 512 MBSubmit: 1331 Solved: 577
[Submit][Status][Discuss]
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
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
6 6 6
1 1 1
2 2 2
2 2 2
HINT
n<=1000000
q<=50000并且保证所有k之和<=2*n
虚树DP。
先是总和,设sum[i]表示以i为根的子树中的路径总和
sum[i]+=sum[u]+size[u]*(k-size[u])*d,d代表路径长度,k代表询问点总数。可以看出这个式子根i这个点是否是询问点没有关系。
因为不管这个点是否是询问点,他的子树中的每个点都要和其他的点组合,根据乘法原理,答案就是这个了,
但若这个点是询问点,那么还要统计其他点到这个点的距离,这里没有统计,但是在递归处理这个点的子树的时候会统计到的。
最大值和最小值类似zd[i]代表i的子树中到i最远的点的距离,则ans1=max(ans1,zd[i]+zd[u]+d),
注意先算答案再更新值,zd[i]=max(zd[i],zd[u]+d)。若这个点是询问点,还要算ans1=max(ans1,zd[x])。最小值类似。
还有,虚树的根要算,不能默认为1,虚树的根为LCA(所有的询问点)。
1 #include <algorithm> 2 #include <iostream> 3 #include <cstdlib> 4 #include <cstring> 5 #include <cstdio> 6 #include <cmath> 7 #define maxn 1000010 8 #define inf 1999999999 9 #define LL long long 10 #define RG register 11 using namespace std; 12 struct data{ 13 int nex,to; 14 }e[maxn*2],g[maxn*2]; 15 int head[maxn],edge=0,a[maxn],dis[maxn],dfn[maxn],head1[maxn],edge1=0,n; 16 LL sum[maxn],zd[maxn],zx[maxn]; 17 LL size[maxn],k,ans1,ans2,s[maxn]; 18 bool bj[maxn]; 19 int f[maxn][20]; 20 inline void add(int from,int to){ 21 e[++edge].nex=head[from]; 22 e[edge].to=to; 23 head[from]=edge; 24 } 25 inline void link(int from,int to){ 26 if(from==to) return; 27 g[++edge1].nex=head1[from]; 28 g[edge1].to=to; 29 head1[from]=edge1; 30 } 31 int de=0; 32 void dfs(int x,int fa){ 33 de++; 34 dfn[x]=de; 35 for(int i=head[x];i;i=e[i].nex){ 36 int u=e[i].to; 37 if(u==fa) continue; 38 f[u][0]=x; 39 dis[u]=dis[x]+1; 40 dfs(u,x); 41 } 42 } 43 inline int lca(int x,int y){ 44 if(dis[x]<dis[y]) swap(x,y); 45 for(int i=18;i>=0;i--) 46 if(dis[f[x][i]]>=dis[y]) x=f[x][i]; 47 if(x==y) return x; 48 for(int i=18;i>=0;i--) 49 if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; 50 return f[x][0]; 51 } 52 bool cmp(const int a,const int b){ 53 return dfn[a]<dfn[b]; 54 } 55 void DP(int x,int fa){ 56 size[x]=bj[x]; 57 zd[x]=0,zx[x]=inf; 58 sum[x]=0; 59 for(RG int i=head1[x];i;i=g[i].nex){ 60 int u=g[i].to; 61 if(u==fa) continue; 62 DP(u,x);LL d=dis[u]-dis[x]; 63 size[x]+=size[u]; 64 sum[x]+=sum[u]+size[u]*(k-size[u])*d; 65 ans1=min(ans1,zx[x]+zx[u]+d),zx[x]=min(zx[x],zx[u]+d); 66 ans2=max(ans2,zd[x]+zd[u]+d),zd[x]=max(zd[x],zd[u]+d); 67 } 68 if(bj[x]) ans1=min(ans1,zx[x]),ans2=max(ans2,zd[x]),zx[x]=0; 69 head1[x]=0;bj[x]=0; 70 return; 71 } 72 inline void build(){ 73 int top=0,LCA; 74 scanf("%lld",&k); 75 for(RG int i=1;i<=k;i++)scanf("%d",&a[i]),bj[a[i]]=1; 76 sort(a+1,a+k+1,cmp); 77 LCA=lca(a[1],a[2]); 78 for(RG int i=3;i<=k;i++) 79 LCA=lca(LCA,a[i]); 80 int root=LCA; 81 for(RG int i=1;i<=k;i++){ 82 if(top==0){s[++top]=a[i];continue;} 83 LCA=lca(s[top],a[i]); 84 while(1){ 85 if(dis[s[top-1]]<=dis[LCA]){ 86 link(s[top],LCA),link(LCA,s[top]); 87 top--; 88 if(s[top]!=LCA) s[++top]=LCA; 89 break; 90 } 91 link(s[top],s[top-1]),link(s[top-1],s[top]),top--; 92 } 93 if(s[top]!=a[i]) s[++top]=a[i]; 94 } 95 while(top>1) link(s[top],s[top-1]),link(s[top-1],s[top]),top--; 96 top--; 97 ans1=inf,ans2=0; 98 DP(root,0); 99 printf("%lld %lld %lld\n",sum[root],ans1,ans2); 100 edge1=0; 101 } 102 int main() 103 { 104 freopen("!.in","r",stdin); 105 freopen("!.out","w",stdout); 106 int qes,x,y; 107 scanf("%d",&n); 108 for(RG int i=1;i<n;i++) 109 scanf("%d%d",&x,&y),add(x,y),add(y,x); 110 dis[1]=1; 111 f[1][0]=1; 112 dfs(1,0); 113 for(RG int i=1;i<=18;i++) 114 for(RG int j=1;j<=n;j++) 115 f[j][i]=f[f[j][i-1]][i-1]; 116 scanf("%d",&qes); 117 for(RG int i=1;i<=qes;i++) 118 build(); 119 return 0; 120 }