图论

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;
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值