一栋楼共有 h 层,有以下操作:1. 向上移动x层2. 向上移动y层3. 向上移动z层4. 回到第一层问可以达到的楼层数量
如果令\(f(i)\)表示表示仅通过操作2和操作3能到达的 \(mod\ x=i\) 的最小楼层
那么就有以下方程:
\[f(i+y)=f(i)+y\]
\[f(i+z)=f(i)+z\]
想一想最短路的转移方程
\[f(v)=f(u)+dis(u,v)\]
是不是很像?
所以我们可以建图,把 \(i+y\) 和 \(i+z\) 看作点
\(y,z\)成为权值,跑spfa算出\(f(i)\)
由于 \(f(i)\) 的数量统计时没有使用第一个操作
所以每增加一个 \(x\) ,答案都会相应地增加
注意边界是闭区间,所以答案再+1,即
\[ans+=(h-f(i))/x+1\]
代码:
#include<bits/stdc++.h>#define ll long longusing namespace std;ll h,x,y,z,ans=0;ll f[100005];struct Edge{ ll next,to,dis;}edge[200005];int cnt=0,head[200005];inline void add_edge(ll from,ll to,ll dis){ edge[++cnt].next=head[from]; edge[cnt].to=to; edge[cnt].dis=dis; head[from]=cnt;}template<class T>inline void read(T &res){ char c;T flag=1; while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0'; while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;}bool vis[100005];queue<int> q;void spfa(){ memset(f,0x3f3f3f3f,sizeof(f)); memset(vis,0,sizeof(vis)); q.push(1); vis[1]=f[1]=1; while(!q.empty()) { int u=q.front(); q.pop(); vis[u]=0; for(register int i=head[u];i;i=edge[i].next) { int v=edge[i].to; if(f[v]>f[u]+edge[i].dis) { f[v]=f[u]+edge[i].dis; if(!vis[v]) { q.push(v); vis[v]=1; } } } }}int main(){ read(h); read(x);read(y);read(z); if(x==1||y==1||z==1){printf("%lld\n",h);return 0;} for(register int i=0;i<x;++i) { add_edge(i,(i+y)%x,y); add_edge(i,(i+z)%x,z); } spfa(); for(register int i=0;i<x;++i) if(f[i]<=h) ans+=(h-f[i])/x+1; printf("%lld\n",ans); return 0;}