题目1
题意:
给定一张带权图,每个点都有权值ai。对于每个点i,找到一个j,使得2*dis(i,j)+aj的值最小。
2
≤
n
≤
2
⋅
1
0
5
,
1
≤
m
≤
2
⋅
1
0
5
,
1
≤
v
i
,
u
i
≤
n
,
v
i
≠
u
i
,
1
≤
w
i
≤
1
0
12
,
1
≤
a
i
≤
1
0
12
2 ≤ n ≤ 2·10^5, 1 ≤ m ≤ 2·10^5,1 ≤ v_i, u_i ≤ n, v_i ≠ u_i, 1 ≤ w_i ≤ 10^{12},1 ≤ a_i ≤ 10^{12}
2 ≤ n ≤ 2⋅105,1 ≤ m ≤ 2⋅105,1 ≤ vi, ui ≤ n, vi = ui,1 ≤ wi ≤ 1012,1 ≤ ai ≤ 1012
分析:
在图上有多个点满足条件的其实就可以往超级源点上面想。我们思考对于一个点i,找任意的j,那么我们把所有的j都集成到超级源点上,超级源点与他们连的边权是多少呢,因为要代价最后要加上点权,所以这个边权就是他们的点权。那么i的答案就是i到超级源点的最短路,所以要求每一个i,就是求超级源点到所有点的最短距离,dijstra即可。
#include <iostream>
#include <vector>
#include <cstring>
#include <queue>
using namespace std;
typedef long long ll;
ll dist[200005],v[200005];
int vis[200005];
struct node{
int num;
ll val;
node(int a,ll b)
{
num = a;
val = b;
}
bool operator<(const node&n) const
{
return val > n.val;
}
};
vector<node> g[200005];
void dij(int begin,int n)
{
priority_queue<node> q;
memset(vis,0,sizeof(vis));
for( int i = 1 ; i <= n ; i++ )
{
dist[i] = 1e18; //设为无穷大
}
dist[begin] = 0;
q.push(node(begin,0)); //把开始节点入队
while( !q.empty() ) //while到队列为空
{
int x = q.top().num;
q.pop();
if( vis[x] ) continue; //如果这个点已经被访问过了,直接跳过
vis[x] = 1;
for( int i = 0 ; i < g[x].size() ; i++ )
{
node t = g[x][i];
if( !vis[t.num] && dist[t.num] > dist[x] + t.val ) //更新花费
{
dist[t.num] = dist[x] + t.val;
t.val = dist[t.num]; //这一步一定要更新入队的节点的value值
q.push(t);
}
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n,m;
cin >> n >> m;
for (int i = 1; i <= m; i++)
{
int x,y;
ll v;
cin >> x >> y >> v;
g[x].push_back(node(y,2*v));
g[y].push_back(node(x,2*v));
}
for (int i = 1; i <= n; i++)
{
cin >> v[i];
g[0].push_back(node(i,v[i]));
}
dij(0,n);
for (int i = 1; i <= n; i++)
{
cout << dist[i];
if( i == n ) cout << '\n';
else cout << ' ';
}
return 0;
}