P4779 【模板】单源最短路径(标准版)
题目背景
2018 年 7 月 19 日,某位同学在 NOI Day 1 T1 归程 一题里非常熟练地使用了一个广为人知的算法求最短路。
然后呢?
100 \rightarrow 60100→60;
Ag \rightarrow CuAg→Cu;
最终,他因此没能与理想的大学达成契约。
小 F 衷心祝愿大家不再重蹈覆辙。
题目描述
给定一个 NN 个点,MM 条有向边的带非负权图,请你计算从 SS 出发,到每个点的距离。
数据保证你能从 SS 出发到任意点。
输入输出格式
输入格式:
第一行为三个正整数 N, M, SN,M,S。 第二行起 MM 行,每行三个非负整数 u_i, v_i, w_iui,vi,wi,表示从 u_iui 到 v_ivi 有一条权值为 w_iwi 的边。
输出格式:
输出一行 NN 个空格分隔的非负整数,表示 SS 到每个点的距离。
输入输出样例
输入样例#1: 复制
4 6 1
1 2 2
2 3 2
2 4 1
1 3 5
3 4 3
1 4 4
输出样例#1: 复制
0 2 4 3
说明
样例解释请参考 数据随机的模板题。
1 \leq N \leq 1000001≤N≤100000;
1 \leq M \leq 2000001≤M≤200000;
S = 1S=1;
1 \leq u_i, v_i\leq N1≤ui,vi≤N;
0 \leq w_i \leq 10 ^ 90≤wi≤109,
0 \leq \sum w_i \leq 10 ^ 90≤∑wi≤109。
模板
#include<bits/stdc++.h>
using namespace std;
#define maxn 100005
#define maxm 200005
#define INF 2147483647
inline int read(){
int x=0,k=1; char c=getchar();
while(c<'0'||c>'9'){if(c=='-')k=-1;c=getchar();}
while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*k;
}
struct Edge
{
int u,v,w,next;
}edge[maxm];
int head[maxm],cnt=1,n,m,s,vis[maxn],dis[maxn];
#define P pair<long long,int>
priority_queue<P,vector<P>,greater<P> >q;
//把最小的元素放在队首的优先队列
inline void add(int u,int v,int w)
{
edge[++cnt].u=u;
//这句话对于此题不需要,但在缩点之类的问题还是有用的
edge[cnt].v=v;
edge[cnt].w=w;
edge[cnt].next=head[u];
//存储该店的下一条边
head[u]=cnt;
//更新目前该点的最后一条边(就是这一条边)
}
//链式前向星加边
void dijkstra()
{
for(int i=1;i<=n;i++)
{
dis[i]=INF;
}
dis[s]=0;
//赋初值
q.push(make_pair(0,s));
while(!q.empty())
//堆为空即为所有点都更新
{
int x=q.top().second;
q.pop();
//记录堆顶并将其弹出
if(!vis[x])
//没有遍历过才需要遍历
{
vis[x]=1;
for(int i=head[x];i;i=edge[i].next)
//搜索堆顶所有连边
{
int v=edge[i].v;
dis[v]=min(dis[v],dis[x]+edge[i].w);
//松弛操作
q.push(make_pair(dis[v],v));
}
}
}
}
int main()
{
n=read(),m=read(),s=read();
for(int i=1;i<=m;i++)
{
int x,y,z;
x=read(),y=read(),z=read();
add(x,y,z);
}
dijkstra();
for(int i=1;i<=n;i++)
{
printf("%d ",dis[i]);
}
return 0;
}
图论一顿套模版(https://ac.nowcoder.com/acm/contest/283/H)
链接:https://ac.nowcoder.com/acm/contest/283/H
来源:牛客网
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld
题目描述
由于临近广西大学建校90周年校庆,西大开始了喜闻乐见的校园修缮工程!
然后问题出现了,西大内部有许许多多的道路,据统计有N栋楼和M条道路(单向),每条路都有“不整洁度”W,现在校方想知道从S楼到T楼的所有路径中,“不整洁度”乘积最小是多少。
由于答案可能很大,所以你需要将最后的答案对109+7取模。
输入描述:
第一行为四个整数N、M、S、T,意义如上。
第2至第M+1行每行表示一条道路,有三个整数,分别表示每条道路的起点u,终点v和“不整洁度”W。
输入保证没有自环,可能有重边。
1≤N≤500001≤N≤50000
1≤M≤1000001≤M≤100000
1≤U,V≤N1≤U,V≤N
1≤W≤10101≤W≤1010
其中W一定是2的整数次幂。
输出描述:
输出一个整数,表示最小的不整洁度之乘积对109+7取模的结果。
若无解请输出 -1
示例1
输入
复制
4 4 1 3
1 2 8
1 3 65536
2 4 2
4 3 16
输出
复制
256
由于权值是2的幂次,所以可以将权值转化成2^k。将k当成权值存下来。最后再求2^dis[t]。
还有一个是利用C++里的log2函数求k
#include<iostream>
#include<algorithm>
#include<queue>
#include<string.h>
#define p pair<long long,int>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn = 100010;
const int maxx = 1e9+7;
struct Edge
{
int u,v,next;
long long w;
}edge[maxn];
int head[maxn],n,m,cnt,s,t;
long long dis[maxn];
bool v[maxn];
priority_queue<p,vector<p>,greater<p> > q;
long long qmi(long long a,long long b,long long mod)
{
long long res=1;
while(b)
{
if(b&1)
res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res%mod;
}
void add(int u,int v,long long w)
{
edge[++cnt].u=u;
edge[cnt].v=v;
edge[cnt].w=w;
edge[cnt].next=head[u];
head[u]=cnt;
}
void disc()
{
for(int i=1;i<=n;i++)
dis[i]=inf;
dis[s]=0;
q.push(make_pair(0,s));
while(!q.empty())
{
int x=q.top().second;
q.pop();
if(!v[x])
{
v[x]=true;
for(int i=head[x];i;i=edge[i].next)
{
int v=edge[i].v;
if(dis[v]>dis[x]+edge[i].w)
{
dis[v]=dis[x]+edge[i].w;
}
q.push(make_pair(dis[v],v));
}
}
}
}
int main()
{
cin >> n >> m >> s >> t;
int x,y,z;
for(int i=1;i<=m;i++)
{
cin >> x >> y >> z;
add(x,y,(int)log2((double)z));
}
disc();
if(dis[t]==inf)
cout << -1 << endl;
else
cout << qmi(2,dis[t],maxx)%maxx << endl;
return 0;
}
P2176 [USACO14FEB]路障Roadblock(https://www.luogu.org/problemnew/show/P2176)
每天早晨,FJ从家中穿过农场走到牛棚。农场由 N 块农田组成,农田通过 M 条双向道路连接,每条路有一定长度。FJ 的房子在 1 号田,牛棚在 N 号田。没有两块田被多条道路连接,以适当的路径顺序总是能在农场任意一对田间行走。当FJ从一块田走到另一块时,总是以总路长最短的道路顺序来走。
FJ 的牛呢,总是不安好心,决定干扰他每天早晨的计划。它们在 M 条路的某一条上安放一叠稻草堆,使这条路的长度加倍。牛希望选择一条路干扰使得FJ 从家到牛棚的路长增加最多。它们请你设计并告诉它们最大增量是多少。
输入输出格式
输入格式:
第 1 行:两个整数 N, M。
第 2 到 M+1 行:第 i+1 行包含三个整数 A_i, B_i, L_i,A_i 和 B_i 表示道路 i 连接的田的编号,L_i 表示路长。
输出格式:
第 1 行:一个整数,表示通过使某条路加倍而得到的最大增量。
输入输出样例
输入样例#1: 复制
5 7 2 1 5 1 3 1 3 2 8 3 5 7 3 4 3 2 4 7 4 5 2
输出样例#1: 复制
2
说明
【样例说明】
若使 3 和 4 之间的道路长加倍,最短路将由 1-3-4-5 变为 1-3-5。
【数据规模和约定】
对于 30%的数据,N <= 70,M <= 1,500。
对于 100%的数据,1 <= N <= 100,1 <= M <= 5,000,1 <= L_i <= 1,000,000。
#include<iostream>
#include<algorithm>
#include<queue>
#include<string.h>
#define p pair<long long,int>
#define inf 2147483647
using namespace std;
const int maxn = 12000;
struct Edge
{
int u,v,next;
long long w;
}edge[maxn];
int head[maxn],node[maxn],num[maxn],path[maxn],n,m,cnt,sum;
long long dis[maxn];
bool v[maxn];
priority_queue<p,vector<p>,greater<p> > q;
void add(int u,int v,long long w)
{
edge[++cnt].u=u;
edge[cnt].v=v;
edge[cnt].w=w;
edge[cnt].next=head[u];
head[u]=cnt;
}
void disc()
{
for(int i=1;i<=n;i++)
dis[i]=inf;
memset(v,0,sizeof(v));
memset(path,0,sizeof(path));
memset(num,0,sizeof(num));
dis[1]=0;
q.push(make_pair(0,1));
while(!q.empty())
{
int x=q.top().second;
q.pop();
if(!v[x])
{
v[x]=true;
for(int i=head[x];i;i=edge[i].next)
{
int v=edge[i].v;
if(dis[v]>dis[x]+edge[i].w)
{
dis[v]=dis[x]+edge[i].w;
path[v]=x,num[v]=i;
}
q.push(make_pair(dis[v],v));
}
}
}
}
int main()
{
cin >> n >> m;
int x,y,z;
for(int i=1;i<=m;i++)
{
cin >> x >> y >> z;
add(x,y,z);
add(y,x,z);
}
disc();
long long ans=dis[n];
long long is=ans;
for(int i=n;i!=0;i=path[i])
{
node[++sum]=num[i];
}
for(int i=1;i<=sum;i++)
{
edge[node[i]].w*=2;
edge[node[i]^1].w*=2;
disc();
ans=max(ans,dis[n]);
edge[node[i]].w/=2;
edge[node[i]^1].w/=2;
}
cout << ans-is << endl;
return 0;
}