【最短路&DP】BZOJ5047空间传送装置

50 篇文章 2 订阅

说实话,第一次打BZOJ的月赛,本来是准备充分的,但由于英语考试,然后班主任又喂了一节课的鸡汤,导致最后只剩下一个半小时。也只做出来这一道题(蒟蒻属性暴露无遗)

题目描述

太空中一共有n座星球,它们之间可以通过空间传送装置进行转移。空间传送装置分为m种,第i种装置可以用4个参数 ai,bi,ci,di 来描述。因为时空抖动的问题,在非整数时刻禁止使用空间传送装置。如果在整数s时刻使用装置,那么需要花费 ((ais+bi)modci)+di 单位时间才能完成传送。现在是s时刻,小Q位于1号星球,请写一个程序计算从1号星球到每个星球最少需要的时间。

第一行包含4个正整数n,m,s,e(2<=n<=100000,1<=m<=50,1<=s<=2000,1<=e<=200000)
分别表示星球的个数、空间传送装置的种类数、当前的时间以及空间传送装置的个数。
接下来m行,每行4个正整数a_i,b_i,c_i,d_i(1<=a_i,b_i,c_i,d_i<=2000),依次描述每种装置的参数。
接下来e行,每行3个正整数u_i,v_i,w_i(1<=u_i,v_i<=n,u_i!=v_i,1<=w_i<=m)
表示从星球u_i可以使用第w_i种装置单向传送到星球v_i。


分析

设我们可以在 i,j 两个时间到达某一个点 i<j
因为这道题的路程消耗与出发时间有关,所以可能会担忧这样会不会i比j还要劣一些。然而其实只要稍加思考,就会发现不存在这样的问题,因为这道题允许等待!i时刻完全可以在原地等到j时刻再出发,这样早到的无论如何都不比晚到的差。这样一来,只要能算出 ((ais+bi)modci)+di 在不同s下的最小值,那么这道题就变成了裸的最短路了。
首先,我们很容易想到,我们需要考虑的出发时间只有 [0,ci) 这个区间,不在这个区间内的,我们把出发时间s mod c_i即可。
那么整个问题就变成了:
统计不同出发时间的分别的路程消耗的最小值。
机制地发现数据范围小了很多:
m<=50,c<=2000
感受到了DP的气息。
首先,我们忽略c-1秒,在0秒到c-2秒中:
对于某个时间t,设其消耗时间为dp[t],
我们可以选择:
等待一秒, dp[t]=dp[t+1]+1
一秒钟都不等,直接从当前时刻出发:即 dp[t]=((ait+bi)modci)+di
转移方程很容易理解,那么问题来了,c-1秒怎么解决?
答案是:暴力呗。。。
只有50个c-1秒,我们等待时间也在 [0,c1] 的范围内,直接每个等待时间枚举一次就好了嘛(亏cch还想了半天怎么高效地算出c-1的消耗)。

附上代码:(考场上脑残地wa了4次,全是因为输出的时候逗逼了)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<climits>
#include<cmath>
#include<vector>
#include<queue>
#define SF scanf
#define PF printf
#define MAXT 55
#define MAXC 2010
#define MAXN 100010
#define MAXM 200010
using namespace std;
int n,m,tot,dist[MAXN],res;
vector<int> d[MAXN],p[MAXN];
bool inq[MAXN];
int dp[MAXT][MAXC];
queue<int> q;
struct node{
    int a,b,c,d;
}s[MAXT];
void prepare(){
    for(int i=1;i<=tot;i++){
        int st=s[i].c-1;
        for(int j=0;j<s[i].c;j++)
            dp[i][st]=min(dp[i][st],((st+j)*s[i].a+s[i].b)%s[i].c+s[i].d+j);
        for(int j=s[i].c-2;j>=0;j--)
            dp[i][j]=min(dp[i][j+1]+1,(j*s[i].a+s[i].b)%s[i].c+s[i].d);
    }
}
void spfa(){
    q.push(1);
    while(!q.empty()){
        int x=q.front();
        q.pop();
        inq[x]=0;
        for(int i=0;i<d[x].size();i++){
            int t=dp[p[x][i]][dist[x]%s[p[x][i]].c];
            int y=d[x][i];
            if(dist[y]==-1||dist[y]>dist[x]+t){
                dist[y]=dist[x]+t;
                if(inq[y]==0){
                    q.push(y);
                    inq[y]=1;
                }
            }
        }
    }
}
int main(){
    memset(dp,0x3f,sizeof dp);
    int st;
    memset(dist,-1,sizeof dist);
    SF("%d%d%d%d",&n,&tot,&st,&m);
    int x,y,z;
    for(int i=1;i<=tot;i++){
        SF("%d%d%d%d",&s[i].a,&s[i].b,&s[i].c,&s[i].d);
        s[i].a%=s[i].c;
        s[i].b%=s[i].c;
    }
    prepare();
    for(int i=1;i<=m;i++){
        SF("%d%d%d",&x,&y,&z);
        d[x].push_back(y);
        p[x].push_back(z);
    }
    dist[1]=st;
    spfa();
    for(int i=2;i<=n;i++)
        if(dist[i]==-1)
            PF("-1\n");
        else
            PF("%d\n",dist[i]-st);//注意审题!是需要的时间!不是到达时间!
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值