[洛谷P3403] 跳楼机
问题描述
Srwudi 的家是一幢 h 层的摩天大楼。由于前来学习的蒟蒻越来越多,srwudi 改造了一个跳楼机,使得访客可以更方便的上楼。
经过改造,srwudi 的跳楼机可以采用以下四种方式移动:
- 向上移动 x 层;
- 向上移动 y 层;
- 向上移动 z 层;
- 回到第一层。
一个月黑风高的大中午,DJL 来到了 srwudi 的家,现在他在 srwudi 家的第一层,碰巧跳楼机也在第一层。DJL 想知道,他可以乘坐跳楼机前往的楼层数。
输入格式
第一行一个整数 h,表示摩天大楼的层数。
第二行三个正整数,分别表示题目中的 x, y, z。
输出格式
一行一个整数,表示 DJL 可以到达的楼层数。
样例输入
15
4 7 9
样例输出
9
数据范围
\(h\le 2^{63}-1,x,y,z\le 10^5\)
解析
不妨只考虑第二和第三两种操作,设 \(f_i\) 表示仅用二三操作能够到达的 \(\bmod x=1\) 的最小楼层。那么我们可以利用 \(f_i\) 将属于 \(x\) 剩余系的楼层到达。至于怎么求,我们有一个显然的DP:
\[f_{(i+y)\bmod x}=f_i+y\\f_{(i+z)\bmod x}=f_i+z
\]
但范围太大。我们可以把这个转化成最短路就能保证复杂度了。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define N 100002
#define M 1000002
#define int long long
using namespace std;
int head[N],ver[M*2],nxt[M*2],edge[M*2],l;
int n,x,y,z,i,dis[N];
void insert(int x,int y,int z)
{
l++;
ver[l]=y;
edge[l]=z;
nxt[l]=head[x];
head[x]=l;
}
void Dijkstra()
{
priority_queue<pair<int,int> > q;
memset(dis,0x3f,sizeof(dis));
q.push(make_pair(-1,1));
dis[1]=1;
while(!q.empty()){
int x=q.top().second,d=-q.top().first;
q.pop();
if(d!=dis[x]) continue;
for(int i=head[x];i;i=nxt[i]){
int y=ver[i];
if(dis[y]>dis[x]+edge[i]){
dis[y]=dis[x]+edge[i];
q.push(make_pair(-dis[y],y));
}
}
}
}
signed main()
{
scanf("%lld%lld%lld%lld",&n,&x,&y,&z);
if(x==1||y==1||z==1){
printf("%lld\n",n);
return 0;
}
for(i=0;i<x;i++) insert(i,(i+y)%x,y),insert(i,(i+z)%x,z);
Dijkstra();
int ans=0;
for(i=0;i<x;i++){
if(dis[i]<=n) ans+=(n-dis[i])/x+1;
}
printf("%lld\n",ans);
return 0;
}