[洛谷 3403] 跳樓機(同餘最短路)
阿新 • • 發佈:2020-11-28
題目
題解
當出現形如“給定 \(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\)
求出 \(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 */
總結
利用同餘設計狀態。
同餘最短路。