牛客练习赛50 D.tokitsukaze and Event (双向最短路)

D.tokitsukaze and Event

链接:https://ac.nowcoder.com/acm/contest/1080/D
来源:牛客网

题目描述

这天,tokitsukaze带着她的舰队去才归一儿海探索。这个海域有n个站点,深海舰队控制着这片海域的m条航线,这些航线连接着这n个点,第i条航线连接着ui,vi两个点。航线都是正确的,也就是说没有重复的航线,也没有任何一个点与自己相连。tokitsukaze的舰队经过第i条航线时,会受到来自深海舰队的ai点伤害。

tokitsukaze可以在某个休息站点将接下来的战斗切换至夜战模式,这样在她的舰队经过第i条航线时,受到的伤害就变为bi,不过一旦切换到夜战模式就不能再次切换回来,所以她必须考虑清楚在哪里切换。

现在有个限时活动。活动难度分为1,2,3,4,…n,在难度1下,tokitsukaze可以在任意站点切换到夜战模式,而在难度2下,不能在站点1切换到夜战模式,在难度3下,不能在站点1,2切换模式…以此类推,即在难度k下,tokitsukaze不能在站点1,2,3,4,5…k-1切换模式。同时,活动还要求在游戏结束时必须处于夜战模式。

现在tokitsukaze的舰队从s点出发,要前往深海大本营所在的t点。请你告诉她,在难度为1,2,3,4,5…n时,她的舰队结束游戏时受到的最小伤害。

输入描述:

第一行包括2个正整数n,m,(2≤n≤105,1≤m≤min(n*(n-1)/2,105))。
接下来m行,每行包括4个正整数u,v,a,b,(1≤u,v≤n,1≤a,b≤109)。
最后一行包括2个正整数s,t,(1≤s,t≤n)。

输出描述:

请你告诉tokitsukaze,在难度为1,2,3,4,5…n时,她的舰队处于夜战模式结束游戏受到的最小伤害。

示例1
输入

4 3
1 4 1 30
1 2 1 10
1 3 20 1
2 3

输出

2
11
21
33

示例2
输入

4 3
1 4 30 1
1 2 10 1
1 3 20 1
3 1

输出

1
1
1
51

分析:

假设切换的点编号为x,起点为s,终点为t
显然最小伤害为s到x的白天模式最短距离+x到t的黑夜模式最短距离
最短距离很容易就能想到最短路算法
以s为起点,边权为白天模式的伤害值跑一遍最短路,最短距离存到d1[]
以t为起点,边权为黑夜模式的伤害值跑一遍最短路,最短距离存到d2[]
在x点切换的最小伤害为d1[x]+d2[x]

题目说难度k下,1-(k-1)不能切换,则只有k和k之后的点可以切换,
那么答案就是k点以及k点之后的d1[x]+d2[x]最小值

从后往前跑一边后缀min,最后依次输出就行了

还有一个坑点就是数据是1e9级别的,一些涉及加法的数组要用long long

ps:
这题我自己刚开始写的时候直接写了两个前向星建两个模式的图。
然后看了别人代码发现一个前向星存两个边权就行了(我太菜了)。

code:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
#include<cmath>
#include<algorithm>
typedef long long ll;
using namespace std;
const int maxm=1e5+5;
const ll inf=(1LL<<60);
int head[maxm],nt[maxm<<1],to[maxm<<1],w1[maxm<<1],w2[maxm<<1],cnt;//w1,w2存两个模式的边权
ll d1[maxm],d2[maxm];
int mark[maxm];
int n,m,s,t;
ll sum[maxm];
void init(){
    memset(head,-1,sizeof head);
    for(int i=0;i<maxm;i++)d1[i]=d2[i]=inf;//设为inf
    cnt=1;
}
void add(int x,int y,int w,int ww){
    cnt++;nt[cnt]=head[x];head[x]=cnt;to[cnt]=y;w1[cnt]=w,w2[cnt]=ww;
}
void spfa(int st,int *w,ll *d){
    memset(mark,0,sizeof mark);
    queue<int>q;
    q.push(st);
    mark[st]=1;
    d[st]=0;
    while(!q.empty()){
        int x=q.front();
        q.pop();
        mark[x]=0;
        for(int i=head[x];i!=-1;i=nt[i]){
            int v=to[i];
            if(d[v]>d[x]+w[i]){
                d[v]=d[x]+w[i];
                if(!mark[v]){
                    mark[v]=1;
                    q.push(v);
                }
            }
        }
    }
}
int main(){
    init();
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        int a,b,c,d;
        cin>>a>>b>>c>>d;
        add(a,b,c,d);
        add(b,a,c,d);
    }
    cin>>s>>t;
    spfa(s,w1,d1);
    spfa(t,w2,d2);
    sum[n]=d1[n]+d2[n];
    for(int i=n-1;i>=1;i--){
        sum[i]=min(sum[i+1],d1[i]+d2[i]);
    }
    for(int i=1;i<=n;i++){
        cout<<sum[i]<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值