题目描述
WZland的国王决定举办一个晚会,这次晚会要求所有的WZland居民都参加,但是他发现WZland的所有城市之间的道路都已经毁坏(平时WZland的居民都在自己的城市里活动,所以他们对于那些道路一点都不关心)。这是一件十分麻烦的事情,因为这个晚会要求所有居民都来参加,于是国王决定要重建道路。
他找出了WZland的地图,他发现WZland有N个城市,有M条破败的双向道路连接着这N个城市。由于周年纪念日就要到来,为了节省时间国王决定修建的道路恰好将这N个城市连接起来。修建一条长度为L的道路,需要花费L个Ws(Ws是WZland的通用货币),国王想要将总的花费降到最少,这样他能有足够多的钱来举办晚会。国王还有一个要求,因为这些道路是同时开始修建的,因此修建完这些道路的总时间等于修建完最长的那条道路的时间(你可以认为所有工人的修建速度是一样的,即一单位时间修建1单位长度的道路),国王想要总的修建时间最少。
由于要举行晚会,国王需要找到一个地点来举办晚会,这同样是一件十分麻烦的事情。因为WZland的每个人都十分懒,他们不愿意多走路(就连在这周年纪念日也不例外)。 WZland的居民每走1单位长度的路就会产生1单位的不高兴度(这就是为什么他们都不愿离开自己城市的原因)。WZland的国王想要这个晚会的不高兴度尽量低(晚会的不高兴度就等于所有参加晚会的人的不高兴度的和),这就要求选取一个合适的地点来举办晚会(WZland的居民要通过即将修建好的道路,来到这个晚会举办地)。
举个例子,如果晚会在城市u举行,城市i有Pi个人,城市u和城市i的距离为D(u,i),那么这些人产生的不高兴度之和为Pi*D(u,i)。
国王把这个任务交给了你,他希望你能找出一个地点,使所有人的不高兴度之和最小。
输入格式
输入数据第一行包含两个整数N和M,表示WZland有N个城市,M条破坏的道路;
第2行到N+1行:第i+1行包含一个整数Pi,表示城市i的居民人数。
第N+2行到N+M+1行,每行三个整数A,B,L(1≤A,B≤N,A≠B),表示A和B之间有一条长度为L的破坏道路。
注意:两个城市之间可能会有多条道路。
输出格式
输出数据包含两行。
第一行两个整数C,T(用一个空格隔开),表示修建道路的最少花费和最少时间。
第二行两个整数U,H(用一个空格隔开),表示晚会的地点(如果有多个地点满足条件,输出编号最小的那个城市)和相应地点所有人的不高兴度之和。
样例数据
样例输入
5 7
3
2
2
1
4
1 5 1
1 3 7
2 1 4
2 3 6
3 4 5
2 4 3
5 4 2
样例输出
11 5
5 29
样例解释
数据范围
对于30%的数据,N≤1000,M≤20000;
对于50%的数据,N≤10000, M≤200000;
对于100%的数据,N≤100000, M≤200000;
对于100%的数据,L≤1000000,Pi≤1000000;
输入数据保证只存在一种合法的修建方案。
输入数据保证所有运算结果在64位整数以内。
题目分析
第一问:显然最小生成树,边较多,需用kruskal,勿用prim
第二问:将第一问最小生成树建出来树形动规。
O(n^2) – 30分算法:
枚举根,对于每一个树根树形动规。
每次树形动规统计当前结点总人数个数以及总不高兴度。
总人数每一次加上读入的人数即可。
总不高兴度统计方法:设当前结点为Now,当前结点的儿子结点为Next=edges[G[Now][i]]
然后每次统计最大值即可
O(2n) – 满分算法:
先按上诉算法以1为根进行一次树形动规,同时在每个结点的儿子处记录下到其父亲结点的边权
然后从1开始进行第二次树形动规,统计出每个结点作为集合点的不高兴度
当前结点的总不高兴度即为
以其父亲作为集合点的不高兴度减去当前结点的儿子人数乘上边权再加上剩下的人数乘上边权
即:f2[Now]=f2[fa]-2*Son[Now]+Value[Now]*(Total-2*Son[Now])
其中f是第一次统计的不高兴度,Son是人数,Value是边权,Total是总人数(输入时累加即可)
源代码
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
inline const long long Get_Int() {
long long num=0,bj=1;
char x=getchar();
while(x<'0'||x>'9') {
if(x=='-')bj=-1;
x=getchar();
}
while(x>='0'&&x<='9') {
num=num*10+x-'0';
x=getchar();
}
return num*bj;
}
struct Edge1 {
int x,y;
long long v;
bool operator < (const Edge1 b) const {
return v<b.v;
}
} a[200005];
struct Edge {
int from,to;
long long dist;
};
int n,m,father[200005];
long long P[200005],Son[200005],Value[200005],f[200005],f2[200005],Total=0,ans=1e100,ansl;
vector<Edge>edges;
vector<int>G[200005];
void AddEdge(int from,int to,long long dist) {
edges.push_back((Edge) {
from,to,dist
});
int cnt=edges.size();
G[from].push_back(cnt-1);
}
int Get_Father(int x) {
if(father[x]==x)return x;
else return father[x]=Get_Father(father[x]);
}
void Kruskal() {
sort(a+1,a+m+1);
long long cnt=0,sum=0,Max=0;
for(int i=1; i<=m; i++) {
int fa=Get_Father(a[i].x),fb=Get_Father(a[i].y);
if(fa!=fb) {
father[fa]=fb;
sum+=a[i].v;
Max=a[i].v;
cnt++;
AddEdge(a[i].x,a[i].y,a[i].v);
AddEdge(a[i].y,a[i].x,a[i].v);
if(cnt>=n-1)break;
}
}
printf("%lld %lld\n",sum,Max);
}
void TreeDp(int Now,int fa) {
Son[Now]=P[Now];
for(int i=0; i<G[Now].size(); i++) {
Edge& e=edges[G[Now][i]];
int Next=e.to;
if(Next==fa)continue;
TreeDp(Next,Now);
Son[Now]+=Son[Next];
Value[Next]=e.dist;
f[Now]+=f[Next]+Son[Next]*e.dist;
}
}
void Dfs(int Now,int fa) {
if(Now==1)f2[Now]=f[Now];
else f2[Now]=f2[fa]+Value[Now]*(Total-2*Son[Now]);
for(int i=0; i<G[Now].size(); i++) {
Edge& e=edges[G[Now][i]];
int Next=e.to;
if(Next==fa)continue;
Dfs(Next,Now);
}
}
int main() {
n=Get_Int();
m=Get_Int();
for(int i=1; i<=n; i++) {
P[i]=Get_Int();
father[i]=i;
Total+=P[i];
}
for(int i=1; i<=m; i++) {
a[i].x=Get_Int();
a[i].y=Get_Int();
a[i].v=Get_Int();
}
Kruskal();
TreeDp(1,-1);
Dfs(1,-1);
for(int i=1; i<=n; i++)
if(f2[i]<ans) {
ans=f2[i];
ansl=i;
}
printf("%lld %lld\n",ansl,ans);
return 0;
}