[洛谷 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]) 了。

注意 (x,y,z le 10^5),所以 (x)(y) 的最小公倍数最大是 (xy le 10^{10}) 不会爆 long long ,也就是说 (dist[i]) (如果不为 (INF) 的话)不会超过 (10^{10})

求出 (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
",ans);
    return 0;
}
/*
15
4 7 9

9
*/

总结

利用同余设计状态。

同余最短路。

原文地址:https://www.cnblogs.com/BaseAI/p/14053302.html