题目描述:
电梯有n层,开始在1层,每次可以向上升a,b,c层,问可以到达的层数。
n<=1018,1<=a,b,c<=100000
题目分析:
把楼层对a取模,按余数分成a类。同一余数的楼层只要最低的那层可达,其它所有都可达。
所以只需要考虑上升b,c层,求出模a余i的可达的最低的楼层。
设
d
i
s
[
i
]
dis[i]
dis[i]表示模a余i可达的最低的楼层,转移是显然的:
d
i
s
[
i
]
=
min
(
d
i
s
[
i
]
,
d
i
s
[
(
i
−
b
)
%
a
+
b
]
,
d
i
s
[
(
i
−
c
)
%
a
+
c
]
)
dis[i]=\min(dis[i],dis[(i-b)\%a+b],dis[(i-c)\%a+c])
dis[i]=min(dis[i],dis[(i−b)%a+b],dis[(i−c)%a+c])
转移有环,把每个类看成一个点,跑最短路即可,这就是同余类最短路。
Code:
#include<cstdio>
#include<cstring>
#include<queue>
#define maxn 100005
#define LL long long
using namespace std;
int a,b,c;
bool inq[maxn];
LL dis[maxn],n,ans;
queue<int>q;
inline void upd(int x,LL y){
if(dis[x]>y){
dis[x]=y;
if(!inq[x]) inq[x]=1,q.push(x);
}
}
void SPFA(){
memset(dis,0x3f,sizeof dis);
dis[1]=1,q.push(1);
while(!q.empty()){
int x=q.front();q.pop(),inq[x]=0;
upd((x+b)%a,dis[x]+b),upd((x+c)%a,dis[x]+c);
}
}
int main()
{
freopen("elevator.in","r",stdin);
freopen("elevator.out","w",stdout);
scanf("%lld%d%d%d",&n,&a,&b,&c);
if(a==1) return printf("%lld\n",n),0;
SPFA();
for(int i=0;i<a;i++) if(dis[i]<=n) ans+=(n-dis[i])/a+1;
printf("%lld\n",ans);
}