同余最短路

跳楼机

可以把ax+by+cz=n,转化为ax+r=n,就是把by+cz整个看成一个余数,那么我们就只需要只要余数有哪些,就可以把所有的n算出来了,因为n很大,但是x很小,所以需要用到同余最短路,同余最短路就是假设r=11,a=6,那么就把11认为是11%6=5,就避免了大数,所以我们把边权看成11就可以了,
点击查看代码
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

const int maxn=1e6+10;
const  ll nil=1e18;

class node{
    public:
        int id;
        ll d;
        node(int _id=0,ll _d=0){id=_id,d=_d;}

        bool operator<(const node &rhs)const{
            return d>rhs.d;
        }
};

int Head[maxn],Next[maxn*2],ver[maxn*2];
ll edge[maxn*2];
int tot,inq[maxn];
ll dis[maxn];

void Insert(int u,int v,ll w){
    ver[++tot]=v;
    Next[tot]=Head[u];
    Head[u]=tot;
    edge[tot]=w;
}

void init(){
    memset(Head,-1,sizeof(Head));
    memset(Next,-1,sizeof(Next));
    memset(ver,-1,sizeof(ver));
    memset(edge,-1,sizeof(edge));
}

void dij(int s,int t,int n){
    priority_queue<node>Q;
    for (int i=0;i<=n;i++) dis[i]=nil;

    dis[s]=1;
    Q.push(node(s,1));

    while(!Q.empty()){
        auto it=Q.top();
        Q.pop();
        int u=it.id;
        if(inq[u]) continue;
        inq[u]=1;
        for (int i=Head[u];i!=-1;i=Next[i]){
            int v=ver[i];
            if(dis[v]>dis[u]+edge[i]) {
                dis[v]=dis[u]+edge[i];
                Q.push(node(v,dis[v]));
            }
        }
    }
    return ;
}

int main(){
    #ifdef lmj_debug
        freopen("1.in","r",stdin);
    #endif
    ll n,x,y,h;
    cin>>h;
    cin>>n>>x>>y;
    if(n==1 || x==1 || y==1) {
        cout<<h<<endl;
        return 0;
    }
    init();
    for (int i=0;i<n;i++){
        Insert(i,(i+x)%n,x);
        Insert(i,(i+y)%n,y);
    }
    dij(0,0,n);
    ll ans=0;
    for (int i=0;i<n;i++){
        if(dis[i]<=h && dis[i]!=nil){
            ans=ans+(h-dis[i])/n+1ll;
        }
    }
    printf("%lld\n",ans);
    return 0;
}

ARC084B Small Multiple

观察到任意一个正整数都可以从 1开始,按照某种顺序执行乘\(10\)、加\(1\)的操作,最终得到\(n\),而其中加\(1\)操作的次数就是这个数的数位和。这提示我们使用最短路。

对于所有\(i\),从\(i\)\(i+1\)连边权为1的边;从\(i\)\(i*10\)连边权为0的边。(点的编号均在模\(n\)意义下)

每个\(n\)的倍数在这个图中都对应了\(1\)号点到\(n\)号点的一条路径,求出\(1\)\(n\)的最短路即可。某些路径不合法(如连续走了\(10\)条边权为\(1\)的边),但这些路径产生的答案一定不优,不影响答案。

时间复杂度\(O(n)\)

点击查看代码
#include <bits/stdc++.h>
using namespace std;

const int maxn=5e5+10;
const int nil=0x3f3f3f3f;

class node{
    public:
        int id,d;
        node(int _id=0,int _d=0){id=_id,d=_d;}

        bool operator<(const node &rhs)const{
            return d>rhs.d;
        }
};

int Head[maxn],Next[maxn*2],ver[maxn*2],edge[maxn*2];
int tot,inq[maxn],dis[maxn];

void Insert(int u,int v,int w){
    ver[++tot]=v;
    Next[tot]=Head[u];
    Head[u]=tot;
    edge[tot]=w;
}

void init(){
    memset(Head,-1,sizeof(Head));
    memset(Next,-1,sizeof(Next));
    memset(ver,-1,sizeof(ver));
    memset(edge,-1,sizeof(edge));
}

int dij(int s,int t,int n){
    priority_queue<node>Q;
    for (int i=0;i<=n;i++) dis[i]=nil;

    dis[s]=1;
    Q.push(node(s,1));

    while(!Q.empty()){
        auto it=Q.top();
        Q.pop();
        int u=it.id;
        if(inq[u]) continue;
        inq[u]=1;
        for (int i=Head[u];i!=-1;i=Next[i]){
            int v=ver[i];
            if(dis[v]>dis[u]+edge[i]) {
                dis[v]=dis[u]+edge[i];
                Q.push(node(v,dis[v]));
            }
        }
    }
    return dis[t];
}

int main(){
    #ifdef lmj_debug
        freopen("1.in","r",stdin);
    #endif
    int n;
    cin>>n;
    init();
    for (int i=0;i<n;i++){
        Insert(i,i*10%n,0);
        Insert(i,(i+1)%n,1);
    }

    printf("%d\n",dij(1,0,n));
    return 0;
}
posted @ 2022-03-21 21:59  lmj_1  阅读(86)  评论(0)    收藏  举报