[HEOI2014]大工程
题目描述
国家有一个大工程,要给一个非常大的交通网络里建一些新的通道。
我们这个国家位置非常特殊,可以看成是一个单位边权的树,城市位于顶点上。
在 2 个国家 a,b 之间建一条新通道需要的代价为树上 a,b 的最短路径。
现在国家有很多个计划,每个计划都是这样,我们选中了 k 个点,然后在它们两两之间 新建 C(k,2)条 新通道。现在对于每个计划,我们想知道: 1.这些新通道的代价和 2.这些新通道中代价最小的是多少 3.这些新通道中代价最大的是多少
输入输出格式
输入格式:
第一行 n 表示点数。
接下来 n-1 行,每行两个数 a,b 表示 a 和 b 之间有一条边。点从 1 开始标号。
接下来一行 q 表示计划数。对每个计划有 2 行,第一行 k 表示这个计划选中了几个点。
第二行用空格隔开的 k 个互不相同的数表示选了哪 k 个点。
输出格式:
输出 q 行,每行三个数分别表示代价和,最小代价,最大代价。
输入输出样例
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
3 3 3 6 6 6 1 1 1 2 2 2 2 2 2
说明
对于第 1,2 个点: n<=10000
对于第 3,4,5 个点: n<=100000,交通网络构成一条链
对于第 6,7 个点: n<=100000
对于第 8,9,10 个点: n<=1000000
对于所有数据, q<=50000并且保证所有k之和<=2*n
看到k的和小于2*n,于是立刻想到建虚树
建出虚树后就dp
size[x]表示x的子树中关键点数
f[x]表示x子树中路径的贡献和
Min[x]表示x子树中离x距离最小的关键点的距离
Max[x]表示最大的距离
最大值和求和很简单
最大值总是要取到叶子节点,虚树中叶子节点总是关键点
答案取当前Max[x]+Max[v]+边权w,然后Max[x]=max(Max[x],Max[v]+d)
求和就考虑一条边的贡献
一条边的贡献次数显然是size[v]*(k-size[v])
所以f[x]+=f[v]+size[v]*(k-size[v])*w
求最小值的话,Min[x]初值正无穷
如果是关键点就直接取它的子树路径最小值,否则就是它的两个儿子的子树路径最小值相加
如果是关键点,更新答案后Min[x]要清0,作为接下来的端点
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 typedef long long lol; 8 const int N=2000005; 9 struct Node 10 { 11 int next,to; 12 }edge[N],edge2[N]; 13 int inf=1e9; 14 int dep[N],fa[N][21],dfn[N],cnt,bin[25],head[N],head2[N],num,ed[N]; 15 int size[N],vis[N],Max[N],Min[N],k,M,ans1,ans2,n,Lca,a[N],s[N],top; 16 lol f[N]; 17 int gi() 18 { 19 char ch=getchar(); 20 int x=0; 21 while (ch<'0'||ch>'9') ch=getchar(); 22 while (ch>='0'&&ch<='9') 23 { 24 x=x*10+ch-'0'; 25 ch=getchar(); 26 } 27 return x; 28 } 29 bool cmp(int a,int b) 30 { 31 return dfn[a]<dfn[b]; 32 } 33 void add(int u,int v) 34 { 35 num++; 36 edge[num].next=head[u]; 37 head[u]=num; 38 edge[num].to=v; 39 } 40 void add2(int u,int v) 41 { 42 if (u==v) return; 43 num++; 44 edge2[num].next=head2[u]; 45 head2[u]=num; 46 edge2[num].to=v; 47 } 48 void dfs(int x,int pa) 49 {int i; 50 dep[x]=dep[pa]+1; 51 dfn[x]=++cnt; 52 for (i=1;bin[i]<=dep[x];i++) 53 fa[x][i]=fa[fa[x][i-1]][i-1]; 54 for (i=head[x];i;i=edge[i].next) 55 { 56 int v=edge[i].to; 57 if (v==pa) continue; 58 fa[v][0]=x; 59 dfs(v,x); 60 } 61 ed[x]=cnt; 62 } 63 int lca(int x,int y) 64 {int as,i; 65 if (dep[x]<dep[y]) swap(x,y); 66 for (i=20;i>=0;i--) 67 if (bin[i]<=dep[x]-dep[y]) 68 x=fa[x][i]; 69 if (x==y) return x; 70 for (i=20;i>=0;i--) 71 { 72 if (fa[x][i]!=fa[y][i]) 73 { 74 x=fa[x][i];y=fa[y][i]; 75 } 76 } 77 return fa[x][0]; 78 } 79 80 void dp(int x) 81 {int i; 82 size[x]=vis[x]; 83 Max[x]=0;Min[x]=inf;f[x]=0; 84 for (i=head2[x];i;i=edge2[i].next) 85 { 86 int v=edge2[i].to,d=dep[v]-dep[x]; 87 dp(v); 88 size[x]+=size[v]; 89 f[x]+=f[v]+1ll*size[v]*(k-size[v])*d; 90 ans1=min(ans1,Min[x]+Min[v]+d);Min[x]=min(Min[x],Min[v]+d); 91 ans2=max(ans2,Max[x]+Max[v]+d);Max[x]=max(Max[x],Max[v]+d); 92 } 93 if (vis[x]) ans1=min(ans1,Min[x]),ans2=max(ans2,Max[x]),Min[x]=0; 94 } 95 int main() 96 {int i,u,v,j,q; 97 cin>>n; 98 bin[0]=1; 99 for (i=1;i<=20;i++) 100 bin[i]=bin[i-1]*2; 101 for (i=1;i<=n-1;i++) 102 { 103 scanf("%d%d",&u,&v); 104 add(u,v);add(v,u); 105 } 106 dfs(1,0); 107 cin>>q; 108 while (q--) 109 { 110 k=gi(); 111 M=k; 112 num=0;ans1=inf;ans2=0; 113 for (i=1;i<=k;i++) 114 a[i]=gi(),vis[a[i]]=1; 115 sort(a+1,a+k+1,cmp); 116 Lca=a[1]; 117 for (i=2;i<=k;i++) 118 if (ed[a[i-1]]<dfn[a[i]]) 119 a[++M]=lca(a[i-1],a[i]),Lca=lca(Lca,a[i]); 120 a[++M]=Lca; 121 sort(a+1,a+M+1,cmp); 122 M=unique(a+1,a+M+1)-a-1; 123 s[++top]=a[1]; 124 for (i=2;i<=M;i++) 125 { 126 while (top&&ed[s[top]]<dfn[a[i]]) top--; 127 add2(s[top],a[i]); 128 s[++top]=a[i]; 129 } 130 dp(Lca); 131 printf("%lld %d %d\n",f[Lca],ans1,ans2); 132 for (i=1;i<=M;i++) 133 vis[a[i]]=head2[a[i]]=0; 134 } 135 }