【洛谷P3403】跳楼机

这次是蓝题!

翻了OI wiki才发现还有个同余最短路这种东西
大为震撼 原来图论还有这种含义 人家本来以为只是在图上解决问题的说
看来还是要多逛OI wiki啊
当年我学OI的时候怎么没这玩意
我们先放题面,便于理解

P3403 跳楼机

题目背景

DJL 为了避免成为一只咸鱼,来找 srwudi 学习压代码的技巧。

题目描述

Srwudi 的家是一幢 \(h\) 层的摩天大楼。由于前来学习的蒟蒻越来越多,srwudi 改造了一个跳楼机,使得访客可以更方便的上楼。

经过改造,srwudi 的跳楼机可以采用以下四种方式移动:

  1. 向上移动 \(x\) 层;
  2. 向上移动 \(y\) 层;
  3. 向上移动 \(z\) 层;
  4. 回到第一层。

一个月黑风高的大中午,DJL 来到了 srwudi 的家,现在他在 srwudi 家的第一层,碰巧跳楼机也在第一层。DJL 想知道,他可以乘坐跳楼机前往的楼层数。

输入格式

第一行一个整数 \(h\),表示摩天大楼的层数。

第二行三个正整数,分别表示题目中的 \(x, y, z\)

输出格式

一行一个整数,表示 DJL 可以到达的楼层数。

输入输出样例 #1

输入 #1

15
4 7 9

输出 #1

9

输入输出样例 #2

输入 #2

33333333333
99005 99002 100000

输出 #2

33302114671

说明/提示

可以到达的楼层有:\(1,5,8,9,10,12,13,14,15\)

\(1 \le h \le 2^{63}-1\)\(1 \le x,y,z \le 10^5\)

解法&&个人感想

其实,这道题是板子题
这是我第二次碰到蓝的板子题 上一次还是分层图
我们来介绍一下这个是什么东西
本题 你如果把h-- 起始楼层记为0的话
那么就是 ax+by+cz=k 这个方程有多少k($ k \le h$) 满足这个方程有非负整数解
为了优化复杂度 我们一般取x为三者中最小值
那么 我们考虑一个同余类 即 \(0 \le i \le x-1\)
这个同余类我们计d[i]为通过第二第三种操作能满足对i同余的最小值 从而从这个同余类出发 有$ {h-d[i]} \over x$ +1(包含d[i]本身)个可以满足条件的数
同时,由于同余类两两之间的交集为空,不需担心一个数被重复计算次数的事情
然后,我们建边的时候就按照add(i,(i+y)%x,y) 表示i->(i+y)%x这个同余类之间有一条长度为y的边
这样就可以求出类似最短路的东西了 起始点设为0 0到n的最短路径当然就是n的最小值
有点难理解对吧 确实是这样的
下面看代码:

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define maxm 200005
#define maxn 100005
using namespace std;
ll x,y,z;
ll head[maxm],nex[maxm],edge[maxm],ver[maxm];
ll h,tot;
ll a[4];
ll d[maxn];
ll vis[maxn];
ll ans=0;
const ll INF=(1ull)<<63-1;
void add(ll x,ll y,ll z){
    ver[++tot]=y;
    edge[tot]=z;
    nex[tot]=head[x];
    head[x]=tot;
}
void spfa(){
    d[0]=0;
    vis[0]=1;
    queue<int>q;
    q.push(0);
    while(!q.empty()){
        int x=q.front();q.pop();
        vis[x]=0;
        for(int i=head[x];i;i=nex[i]){
            int y=ver[i],z=edge[i];
            if(d[y]>d[x]+z){
                d[y]=d[x]+z;
                if(!vis[y]){
                    vis[y]=1;
                    q.push(y);
                }
            }
        }
    }
}
int main(){
    scanf("%lld",&h);
    scanf("%lld%lld%lld",&x,&y,&z);
    if(x==1||y==1||z==1){
        printf("%lld",h);
        system("pause");
        return 0;
    }
    h-=1;
    for(int i=0;i<=x-1;i++){
        add(i,(i+y)%x,y);
        add(i,(i+z)%x,z);
        d[i]=INF;
    }
    spfa();
    for(int i=0;i<=x-1;i++){
        if(h>=d[i]) ans+=(h-d[i])/x+1;
    }
    printf("%lld",ans);
    system("pause");
    return 0;
}

变式题型:洛谷P2731 墨墨的等式

晚上刚写完 代码放这:

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define maxm 6000006
#define maxn 500005
using namespace std;
int n,tot;
ll l,r;
int a[15];
ll ver[maxm],nex[maxm],head[maxm],edge[maxm];
int vis[maxn];
ll d[maxn];
const ll INF=(1ull)<<63-1;
void add(ll x,ll y,ll z){
    ver[++tot]=y;
    edge[tot]=z;
    nex[tot]=head[x];
    head[x]=tot;
}
void dijkstra(){
    queue<int>q;
    vis[0]=1;
    d[0]=0;
    q.push(0);
    while(!q.empty()){
        int x=q.front();q.pop();
        vis[x]=0;
        for(int i=head[x];i;i=nex[i]){
            int y=ver[i],z=edge[i];
            if(d[y]>d[x]+z){
                d[y]=d[x]+z;
                if(!vis[y]){
                    vis[y]=1;
                    q.push(y);
                }
            }
        }
    }
}
int main(){
    scanf("%d%lld%lld",&n,&l,&r);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    if(n==1){
        ll ans=0;
        if(r>=a[1]) ans+=r/a[1];
        if(l>=a[1]){
            if(l%a[1]==0) ans-=l/a[1]-1;
            else ans-=l/a[1];
        }
        printf("%lld",ans);
        system("pause");
        return 0;
    }
    int flag=0;
    for(int i=1;i<=n;i++){
        if(a[i]==1){
            flag=1;
            break;
        }
    }
    if(flag==1){
        printf("%lld",r-l+1);
        system("pause");
        return 0;
    }
    l-=1;
    sort(a+1,a+1+n);
    int res=0;
    for(int i=1;i<=n;i++){
        if(a[i]==0) continue;
        else{
            res=i;
            break;
        }
    }
    for(int i=0;i<=a[res]-1;i++){
        for(int j=res+1;j<=n;j++){
            add(i,(i+a[j])%a[res],a[j]);    
            d[(i+a[j])%a[res]]=INF;
        }
    }
    dijkstra();
    ll ans=0;
    for(int i=0;i<=a[res]-1;i++){
        if(r>=d[i]) ans+=(r-d[i])/a[res]+1;
        if(l>=d[i]) ans-=(l-d[i])/a[res]+1;
    }
    printf("%lld",ans);
    system("pause");
    return 0;
}

解法&&个人感想

l-=1 化归为上题
本题需要特判 n=1 以及有零有一的情况

放张团子
image

posted @ 2025-04-02 14:27  elainafan  阅读(46)  评论(0)    收藏  举报