1. 程式人生 > 實用技巧 >[洛谷P3403] 跳樓機

[洛谷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;
}