首先什么是最短路树:最短路树是指通过Dijkstra算法在求出以某一个点为起点的最短路之后,以这个起点为树的根节点所构造的一颗树,这棵树满足如下条件,即:树上某点到根节点的路径对应原图上的一条最短路径。
题目链接:[USACO19JAN]Shortcut G - 洛谷
所以在知道上述知识后我们来看这道题,题目要求在一号点和某个点直接连一条边,使得牛在到所有牛在前往一号的过程中少走的路最大,题目强调了牛可以走新边的条件:(1)走了这条路会比原最短路径更短,(2)这条边出现在原最短路径的某个节点上。如果不满足第二个条件即使存在一条更短的路径牛也不会走,(3)牛在源图中的最短路径还需满足字典序最小的条件。
我们来分析如何解决这道题:首先我们先跑最短路,跑完之后我们考虑建树,因为牛走的路径一定是字典序最小的所以我们在建树的时候枚举这个点的每一条出边判断一下终点是否满足这个条件。
建完树之后我们得到的是一颗以1号点为根节点的深度递增的一颗有向树,那么我们在 i 号节点加一条到1号点的边那么受影响的只有i号点以及i号点的所有儿子们,那么加这条边所少走的路总和为: (这个节点以及下面儿子数量之和) * (i号点到一号点原来的最短路径长度 - 新边的长度)。
这样我们的单次查询的时间复杂度就是O(1)的所以整个算法的最大时间复杂的就是我们最开始跑的最短路。这样我们就轻松的AC这道题了
注意:long long
code:
#include <bits/stdc++.h>
#define N 1000010
#define int long long
using namespace std;
struct node {
int f,t,nex,val;
}rt[N],rt1[N];
int head[N],head1[N];
int cnt,cnt1;
int a,b,vall;
int val[N];
int num[N];
int siz[N];
priority_queue<pair<int ,int > > p;
void add(int x,int y,int z ) { //建原图的边
rt[++ cnt].f = x;
rt[cnt].t = y;
rt[cnt].val = z;
rt[cnt].nex = head[x];
head[x] = cnt;
}
void add1(int x,int y) { //建树的边
rt1[++ cnt1].f = x;
rt1[cnt1].t = y;
rt1[cnt1].nex = head1[x];
head1[x] = cnt1;
}
void dfs(int x) { //树上dfs统计儿子的个数
siz[x] = num[x];
for(int i = head1[x];i;i = rt1[i].nex) {
dfs(rt1[i].t);
siz[x] += siz[rt1[i].t];
}
}
signed main() {
cin>>a>>b>>vall;
for(int i = 1;i <= a;i ++) {
cin>>num[i];
}
int x,y,z;
for(int i = 1;i <= b;i ++) {
cin>>x>>y>>z;
add(x,y,z);
add(y,x,z);
}
memset(val,0x3f,sizeof val);
val[1] = 0;
p.push(make_pair(0,1));
while(!p.empty()) { //先跑最短路
int faq = p.top().second;
p.pop();
for(int i = head[faq];i;i = rt[i].nex) {
if(val[rt[i].t] > val[faq] + rt[i].val) {
val[rt[i].t] = val[faq] + rt[i].val;
p.push(make_pair(-val[rt[i].t],rt[i].t));
}
}
}
for(int i = 1;i <= a;i ++) {
int minn = 99999999;
for(int j = head[i];j;j = rt[j].nex) { //我们开始建树(取min的操作就是满足一个字典序最小)
if(val[rt[j].t] + rt[j].val == val[i]) {
minn = min(minn,rt[j].t);
}
}
if(minn <= a)
add1(minn,i);
}
dfs(1); //dfs统计儿子的数量
int maxx = 0;
for(int i = 2;i <= a;i ++) { //每次我们都计算一下这条新边的贡献
int qaq = 0;
qaq += (val[i] - vall) * siz[i];
maxx = max(maxx,qaq);
}
cout<<maxx;
return 0;
}
/*
5 6 2
1 2 3 4 5
1 2 5
1 3 3
2 4 3
3 4 5
4 5 2
3 5 7
* */