Luogu P3403 跳楼机|同余最短路

题意:给出跳楼机的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;
}

习题[国家集训队]墨墨的等式 BZOJ链接


参考资料
【1】https://www.luogu.org/problemnew/solution/P3403

转载于:https://www.cnblogs.com/fmj123/p/Luogu3403.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值