[洛谷P3403] 跳楼机

问题描述

Srwudi 的家是一幢 h 层的摩天大楼。由于前来学习的蒟蒻越来越多,srwudi 改造了一个跳楼机,使得访客可以更方便的上楼。

经过改造,srwudi 的跳楼机可以采用以下四种方式移动:

  1. 向上移动 x 层;
  2. 向上移动 y 层;
  3. 向上移动 z 层;
  4. 回到第一层。

一个月黑风高的大中午,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;
}
posted @ 2020-08-23 18:17  CJlzf  阅读(167)  评论(0编辑  收藏  举报