Description
给你一个有向连通图G,每点有个权值Di(0<Di),要求生成一棵树根为1号节点的有根树T。对于树中边E,E的代价为所有从根出发的且包含E的路径的终点权值的和。现求生成树T,使得边的代价总和最小。
Input
第一行N,M分别为点数,边数。(0<=N <= 20000;0<=M <= 200000)
接下来M行,每行两个数U,V描述边的两个端点,即从U到V有一条有向边。
最后一行N个数,顺次给出每个点的权值。
Output
一个数,最小代价。
Sample Input
5 4
1 2
1 3
3 4
3 5
1 2 3 4 5
Sample Output
23
Hint
样例解释:
如图只有一种生成树的方法,求得代价为23。
数据规模:
所有数据保证不会超过长整型(C++中的int)。
题解
归纳发现,算出的总代价就是每个节点在生成树中的深度$×$点权的和。
我们用贪心的思想,每个点的深度都要尽可能小。那么我们只需以$1$号节点为源点,跑一遍最短路即可。
由最小生成树的思想,我们易知所有求出的最短路径都在一棵生成树上,满足题意。
1 #include<map> 2 #include<queue> 3 #include<stack> 4 #include<cmath> 5 #include<ctime> 6 #include<cstdio> 7 #include<string> 8 #include<cstring> 9 #include<cstdlib> 10 #include<iostream> 11 #include<algorithm> 12 using namespace std; 13 const int N=20000; 14 const int M=200000; 15 16 int n,m,u,v; 17 struct tt 18 { 19 int to,next; 20 }edge[M+5]; 21 int path[N+5],top; 22 int ans; 23 void Add(int u,int v); 24 25 int dist[N+5]; 26 bool vis[N+5]; 27 void SPFA(); 28 29 int main() 30 { 31 scanf("%d%d",&n,&m); 32 for (int i=1;i<=m;i++) 33 { 34 scanf("%d%d",&u,&v); 35 Add(u,v); 36 } 37 SPFA(); 38 for (int i=1;i<=n;i++) 39 { 40 scanf("%d",&u); 41 ans+=u*dist[i]; 42 } 43 printf("%d ",ans); 44 return 0; 45 } 46 47 void Add(int u,int v) 48 { 49 edge[++top].to=v; 50 edge[top].next=path[u]; 51 path[u]=top; 52 } 53 void SPFA() 54 { 55 memset(dist,127/3,sizeof(dist)); 56 dist[1]=0; 57 vis[1]=1; 58 queue<int>Q; 59 Q.push(1); 60 while (!Q.empty()) 61 { 62 for (int i=path[Q.front()];i;i=edge[i].next) 63 { 64 if (dist[edge[i].to]>dist[Q.front()]+1) 65 { 66 dist[edge[i].to]=dist[Q.front()]+1; 67 if (!vis[edge[i].to]) 68 { 69 Q.push(edge[i].to); 70 vis[edge[i].to]=1; 71 } 72 } 73 } 74 vis[Q.front()]=0; 75 Q.pop(); 76 } 77 }