用友汽車科創板上市通過
阿新 • • 發佈:2022-03-07
計組課划水研究了一下(
參考:OI Wiki
介紹
常見題型
- 給定 n 個整數,求這 n 個整數能拼湊出多少的其他整數( n 個整數可以重複取)
- 給定 n 個整數,求這 n 個整數不能拼湊出的最小(最大)的整數
- 至少要拼幾次才能拼出模 k 餘 p 的數
簡介
同餘最短路利用同餘來構造一些狀態,可以達到優化空間複雜度的目的
類比差分約束方法,利用同餘構造的這些狀態可以看作單源最短路中的點。同餘最短路的狀態轉移通常是這樣的 f(i+y) = f(i) + y,類似單源最短路中 f(v) = f(u) + edge(u,v)
例題
P3403 跳樓機
傳送門:https://www.luogu.com.cn/problem/P3403
題目大意:給定x, y, z, h,對於 k ∈ [1,h],有多少個 k 能滿足 ax + by + cz = k
令 dis(i) 表示僅通過 操作 2 和 操作 3 能到達的 mod x = i 的最小樓層
可以得到如下兩個狀態:
- dis(i + y) = dis(i) + y
- dis(i + z) = dis(i) + z
那麼實際上相當於執行了最短路中的建邊操作:
- add(i, (i + y) % x, y)
- add(i, (i + z) % x, z)
跑一次最短路就可以求出相應的 dis(i),答案即為 ans += (h − f[i]) / x + 1(+1 是因為當前所在樓層也算)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 100010; const int INF = 0x3f3f3f3f; ll h, x, y, z; ll head[maxn << 1], tot; ll dis[maxn], vis[maxn]; queue<int> q; struct edge { ll to, next, w; } e[maxn << 1]; void add(ll u, ll v, ll w) { e[++tot] = edge{v, head[u], w}; head[u] = tot; } void spfa() { dis[1] = 1; vis[1] = 1; q.push(1); while (!q.empty()) { int u = q.front(); q.pop(); vis[u] = 0; for (int i = head[u]; i; i = e[i].next) { int v = e[i].to, w = e[i].w; if (dis[v] > dis[u] + w) { dis[v] = dis[u] + w; if (!vis[v]) { q.push(v); vis[v] = 1; } } } } } int main() { memset(dis, INF, sizeof(dis)); scanf("%lld", &h); scanf("%lld %lld %lld", &x, &y, &z); if (x == 1 || y == 1 || z == 1) { printf("%d\n", h); return 0; } for (int i = 0; i < x; i++) { add(i, (i + z) % x, z); add(i, (i + y) % x, y); } spfa(); ll ans = 0; for (int i = 0; i < x; i++) if (h >= dis[i]) ans += (h - dis[i]) / x + 1; printf("%lld\n", ans); return 0; }