题意:给出跳楼机的4个操作,分别为
1.向上移动\(x\)层;
2.向上移动\(y\)层;
3.向上移动\(z\)层;
4.回到第一层。 显然,并不需要
求从第一层开始,能到达\(1\)到\(h\)中的多少层?
\(1<=h<=2^{63}-1\)
\(1<=x, y, z<=100000\)
题解:
好像可以直接\(DP\)?
布星啊,看下数据范围。
那先来推推定理?
接下来假设\(x\le y\le z\)
对于一个数\(k\),若它能到达,则\(k+x,k+2x,k+...\)(均\(\le h\))均能到达,\(y\),\(z\)同理
那我们可以选\(x\)为模数,若\(k\)可以,上面的都可以!显然可以设\(f[i]\)表示最小的可以用\(y,z\)表示出来的数。计算答案只需求\(\sum\limits^{x-1}_{i=0}(h-f[i])/x+1\),空间是质的飞跃。
等等,你这根本不是递推啊,dp个鬼啊。
好吧,确实不行,但看下转移?
\(f[i+y]=f[i]+y,f[i+z]=f[i]+z\)
似曾相识有没有?
这与最短路非常相像完 全 一 致!
那就用最短路代替,从\(i->(i+y)\%x\)边权\(y\),从\(i->(i+z)\%x\)边权\(z\)。
自此,算法成型。
这就是同余系最短路了
tips:应从\(1\%x\)开始跑最短路!
#include<bits/stdc++.h>
using namespace std;
long long cc,to[300100],net[300100],fr[300100],l[300100],g;
long long ans,hh,f[300100],h[300100],ha[300100],x[4];
bool vis[300100];
void addedge(long long u,long long v,long long len)
{
cc++;
to[cc]=v;net[cc]=fr[u];fr[u]=cc;l[cc]=len;
}
void add(long long x,long long y)
{
g++;
h[g]=x;ha[g]=y;
long long fa=g/2,so=g;
while (h[fa]>h[so]&&fa)
{
swap(h[fa],h[so]);
swap(ha[fa],ha[so]);
so=fa;fa/=2;
}
}
long long del()
{
long long re=ha[1];
h[1]=h[g];ha[1]=ha[g];g--;
long long fa=1,so=2;
if (h[so]>h[so+1]&&so+1<=g) so++;
while (h[fa]>h[so]&&so<=g)
{
swap(h[fa],h[so]);
swap(ha[fa],ha[so]);
fa=so;so*=2;
if (h[so]>h[so+1]&&so+1<=g) so++;
}
return re;
}
void dij()
{
for (long long i=0;i<x[0];i++)
vis[i]=false,f[i]=9223372036854775807;
f[1%x[0]]=1;
add(1,1%x[0]);
while (g)
{
long long x=del();
if (vis[x]) continue;
vis[x]=true;
for (long long i=fr[x];i;i=net[i])
{
if (f[to[i]]>f[x]+l[i])
{
f[to[i]]=f[x]+l[i];
add(f[to[i]],to[i]);
}
}
}
}
int main()
{
cin>>hh;
cin>>x[0]>>x[1]>>x[2];
sort(x+0,x+3);
for (long long i=0;i<x[0];i++)
{
addedge(i,(i+x[1])%x[0],x[1]);
addedge(i,(i+x[2])%x[0],x[2]);
}
dij();
for (long long i=0;i<x[0];i++)
{
if (f[i]>hh) continue;
ans+=(hh-f[i])/x[0]+1;
}
cout<<ans<<endl;
return 0;
}
参考资料
【1】https://www.luogu.org/problemnew/solution/P3403
原文地址:https://www.cnblogs.com/fmj123/p/Luogu3403.html
时间: 2024-10-30 09:59:28