3611: [Heoi2014]大工程
Time Limit: 60 Sec Memory Limit: 512 MBSubmit: 408 Solved: 190
[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
Source
【思路】
虚树+树上DP
对每次询问构造虚树,在虚树上进行DP。
ans1和ans2即树上的最长/短链问题,利用前缀和思想可以求解。
设sum[x] = ∑(sum[y] + w * size[y]); 则有
ans += ∑((sum[y] + w * size[y]) * (size[x] - size[y]));
其中size[x]表示以x为根的子树包含的询问点数目,w为x->y的边长。
好大的工程=-=
【代码】
1 #include<cstdio> 2 #include<vector> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 using namespace std; 7 8 const int N = 1000000+10; 9 const int INF = 1e9+1e9; 10 const int D = 22; 11 12 typedef long long LL; 13 vector<int> G[N],g[N]; 14 int d[N],dfn[N]; 15 int n,q,dfsc; 16 17 void adde(int u,int v) { 18 if(u!=v) G[u].push_back(v); else return; 19 } 20 bool cmp(const int& lhs,const int& rhs) { 21 return dfn[lhs]<dfn[rhs]; 22 } 23 ////////////////////////////////////////////////////lca which cuts down about 5000ms 24 int siz[N],top[N],son[N],fa[N]; 25 void dfs1(int u) { 26 siz[u]=1,son[0]=0; dfn[u]=++dfsc; 27 for(int i=0;i<g[u].size();i++) { 28 int v=g[u][i]; 29 if(v!=fa[u]) { 30 fa[v]=u; d[v]=d[u]+1; 31 dfs1(v); 32 siz[u]+=siz[v]; 33 if(siz[v]>siz[son[u]]) son[u]=v; 34 } 35 } 36 } 37 void dfs2(int u,int tp) { 38 top[u]=tp; 39 if(son[u]) dfs2(son[u],tp); 40 for(int i=0;i<g[u].size();i++) { 41 int v=g[u][i]; 42 if(v!=son[u] && v!=fa[u]) dfs2(v,v); 43 } 44 } 45 int LCA(int u,int v) { 46 while(top[u]!=top[v]) 47 if(d[top[u]]>=d[top[v]]) u=fa[top[u]]; 48 else v=fa[top[v]]; 49 return d[u]<d[v]? u:v; 50 } 51 //////////////////////////////////////////////// 52 LL sum[N],ans; 53 int ans1,ans2,mi[N],mx[N],sz[N]; bool ifq[N]; 54 int dp(int u) { 55 sum[u]=0; sz[u]=ifq[u]; 56 mi[u]=ifq[u]? 0:INF; 57 mx[u]=ifq[u]? 0:-INF; 58 for(int i=0;i<G[u].size();i++) { 59 int v=G[u][i],w=d[v]-d[u]; 60 dp(v); 61 sz[u]+=sz[v]; 62 sum[u]+=sum[v]+sz[v]*w; 63 ans1=min(ans1,mi[u]+mi[v]+w); 64 ans2=max(ans2,mx[u]+mx[v]+w); 65 mi[u]=min(mi[u],mi[v]+w); 66 mx[u]=max(mx[u],mx[v]+w); 67 } 68 for(int i=0;i<G[u].size();i++) { 69 int v=G[u][i],w=d[v]-d[u]; 70 ans+=(sum[v]+sz[v]*w)*(sz[u]-sz[v]); 71 } 72 ifq[u]=0; G[u].clear(); //clear 73 } 74 void read(int& x) { 75 char c=getchar();while(!isdigit(c))c=getchar(); 76 x=0;while(isdigit(c))x=x*10+c-'0' , c=getchar(); 77 } 78 void solve() { 79 int tot=0,top=0,k; 80 static int st[N],h[N]; 81 read(k); 82 for(int i=1;i<=k;i++) read(h[i]),ifq[h[i]]=1; 83 sort(h+1,h+k+1,cmp); 84 st[++top]=1; 85 for(int i=1;i<=k;i++) { 86 int p=h[i],lca=LCA(p,st[top]); 87 for(;;) { 88 if(d[lca]>=d[st[top-1]]) { 89 adde(lca,st[top]); 90 top--; 91 if(st[top]!=lca) st[++top]=lca; 92 break; 93 } 94 adde(st[top-1],st[top]); top--; 95 } 96 if(st[top]!=p) st[++top]=p; 97 } 98 while(--top) adde(st[top],st[top+1]); 99 ans=ans2=0 , ans1=INF; 100 dp(1); 101 printf("%lld %d %d ",ans,ans1,ans2); 102 } 103 int main() { 104 read(n); 105 int u,v; 106 for(int i=1;i<n;i++) { 107 read(u),read(v); 108 g[u].push_back(v) , g[v].push_back(u); 109 } 110 d[1]=1; dfs1(1); dfs2(1,1); 111 read(q); 112 while(q--) solve(); 113 return 0; 114 }