跳楼机(最短路)
跳楼机
题目背景
DJL为了避免成为一只咸鱼,来找srwudi学习压代码的技巧。
题目描述
Srwudi的家是一幢h层的摩天大楼。由于前来学习的蒟蒻越来越多,srwudi改造了一个跳楼机,使得访客可以更方便的上楼。
经过改造,srwudi的跳楼机可以采用以下四种方式移动:
-
向上移动x层;
-
向上移动y层;
-
向上移动z层;
-
回到第一层。
一个月黑风高的大中午,DJL来到了srwudi的家,现在他在srwudi家的第一层,碰巧跳楼机也在第一层。DJL想知道,他可以乘坐跳楼机前往的楼层数。
输入输出格式
输入格式:
第一行一个整数h,表示摩天大楼的层数。
第二行三个正整数,分别表示题目中的x, y, z。
输出格式:
一行一个整数,表示DJL可以到达的楼层数。
输入输出样例
输入样例#1: 复制
15
4 7 9
输出样例#1: 复制
9
输入样例#2: 复制
33333333333
99005 99002 100000
输出样例#2: 复制
33302114671
说明
可以到达的楼层有:1,5,8,9,10,12,13,14,15
想不出来不要死磕这一题,先看看第三题。。。。
1<=h<=2^63-1
1<=x, y, z<=100000
题解
这是一道思路非常奇妙的题目。
我去百度了一下,发现题解都不是很清楚。
我也不知道我能不能说清楚。
首先设原方程为ax+by+cz=[1,h]。
我们保证x有解。那么by+cz=[1,h]%x。
接下来我们假设一个点是走y步可以到的。
那么每走一次y步,就可以往后一直用x步走。
走一次z步同理。
把y和z单独拿出来看。
枚举0到x-1的点。(i+y)%x就是当x走不了的情况下,y可以走到的楼层。
而(i+y)%x需要走多少次y由i—>(i+y)%x的最短路推出来。
z同理。
那么你可以会问方程是by+cz。
而我们只单独讨论了。
实际上这已经在最短路上跑了出来。因为0到x-1是共享的。
用dis[i]表示到i的最短距离。那么贡献便是(H-dis[i])/x+1的。
代码
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<queue>
#define ll long long
using namespace std;
const int N=500001;
ll H,x,y,z;
ll vis[N],dis[N];
ll num,head[N],ans;
struct node{
int to,v,nex;
}e[N];
void add(int from,int to,int v){
num++;
e[num].to=to;
e[num].v=v;
e[num].nex=head[from];
head[from]=num;
}
ll read(){
ll x=0,w=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*w;
}
void dijkstra(){
priority_queue<pair<ll,int> >q;
memset(dis,63,sizeof(dis));dis[1%x]=1;
q.push(make_pair(0,1%x));
while(!q.empty()){
int u=q.top().second;q.pop();
if(vis[u])continue;vis[u]=1;
for(int i=head[u];i;i=e[i].nex){
int v=e[i].to;
if(dis[v]>dis[u]+e[i].v){
dis[v]=dis[u]+e[i].v;
q.push(make_pair(-dis[v],v));
}
}
}
}
int main(){
H=read();
x=read();y=read();z=read();
if(x==1)printf("%lld\n",H),exit(0);
for(int i=0;i<x;i++){
add(i,(i+y)%x,y);
add(i,(i+z)%x,z);
}
dijkstra();
for(int i=0;i<x;i++)if(dis[i]<=H)ans+=(H-dis[i])/x+1;
printf("%lld\n",ans);
return 0;
}