1. 程式人生 > 實用技巧 >[洛谷 3403] 跳樓機(同餘最短路)

[洛谷 3403] 跳樓機(同餘最短路)

題目

題目地址

題解

當出現形如“給定 \(n\) 個整數,求這 \(n\) 個整數能拼湊出多少的其他整數(\(n\) 個整數可以重複取)”,以及“給定 \(n\) 個整數,求這 \(n\) 個整數不能拼湊出的最小(最大)的整數”的問題時可以使用同餘最短路的方法。

引自 OI-wiki。

不妨設 \(x < y < z\)。(為了減少狀態)

\(dist[i]\) 為最小的 \(p=ay+bz\)\(p \equiv i \pmod {x}\)

則可以對於每個 \(i\),連一條 \(i->(i+y)\%x\) 的邊,邊權為 \(y\),連一條 \(i->(i+z)\%x\)

的的邊,邊權為 \(z\)。在以 \(1\) 為起點這個圖上跑最短路,就可以求出 \(dist[i]\) 了。

求出 \(dist[i]\) 後,可以發現 \(dist[i]+kx,k \in Z\) 都可以到達(這就是設計同餘狀態的意義),於是就可以算出可以到多少個樓層了。

時間複雜度 \(O(n \log n)\)

程式碼

#include<bits/stdc++.h>
#define int long long
#define mp make_pair
#define fi first
#define se second
using namespace std;
inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while(ch<'0' || ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { x=(x<<3)+(x<<1)+(ch^48); ch=getchar(); }
    return x * f;
}
typedef pair<int,int> PII;
const int N = 1e5+7;
int h,x,y,z,cnt;
int head[N],dist[N];
struct Edge {
	int next,to,w;
}edge[N<<1];
inline void add(int u,int v,int w) {
	edge[++cnt] = (Edge)<%head[u],v,w%>;
	head[u] = cnt;
}
bool vis[N];
void Dijkstra() {
	memset(dist, -1, sizeof(dist));
	priority_queue<PII> q;
	dist[1] = 1;
	q.push(mp(dist[1],1));
	while(!q.empty()) {
		int u = q.top().se;
		q.pop();
		if(vis[u]) continue;
		vis[u] = 1;
		for(int i=head[u];i;i=edge[i].next) {
			int v = edge[i].to, w = edge[i].w;
			if(dist[v]==-1 || dist[v]>dist[u]+w) {
				dist[v] = dist[u] + w;
				q.push(mp(-dist[v],v));
			}
		}
	}
}
signed main()
{
	h = read(), x = read(), y = read(), z = read();
	if(x > y) swap(x,y);
	if(x > z) swap(x,z);
	for(int i=0;i<x;++i) {
		add(i,(i+y)%x,y);
		add(i,(i+z)%x,z);
	}
	Dijkstra();
	int ans = 0;
	for(int i=0;i<x;++i) {
		if(dist[i] > -1) {
			if(h >= dist[i]) {
				ans += (h-dist[i])/x + 1;
			}
		}
	}
	printf("%lld\n",ans);
    return 0;
}
/*
15
4 7 9

9
*/

總結

利用同餘設計狀態。

同餘最短路。