一栋楼共有 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 long
using 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
",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
",ans);
return 0;
}