DestinHistoire

 

P3403 跳楼机(同余最短路)

题目描述

  给出 \(x,y,z,h\),对于 \(k\in [1,h]\),求有多少个 \(k\) 能够满足 \(1+ax+by+cz=\)(\(1\leq h\leq 2^{63}-1,1\leq x,y,z\leq 10^5\))。

分析

  不妨设 \(x<y<z\)。

  设 \(d_i\) 为只通过 \(+y\) 和 \(+z\) 两种操作能够到达的最低楼层 \(m\)(初始楼层为 \(1\)),且 \(m\mod x=i\)。

  则可以得到两个状态:

  • \(i\stackrel{y}{\rightarrow}(i+y)\mod x\)
  • \(i\stackrel{z}{\rightarrow}(i+z)\mod x\)

  这实际上相当于执行了最短路中的建边操作:

add_edge(i,(i+y)%x,y);
add_edge(i,(i+z)%x,z);

  接下来只需要求出 \(d_0,d_1,d_2,\cdots,d_{x-1}\),只需要跑一次最短路就可以求出相应的 \(d_i\),答案为:

\[\sum_{i=0}^{x-1}\Big(\frac{h-d_i}{x}+1\Big) \]

  \(+1\) 是因为当前所在楼层也算一次。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=4e5+10;
const int INF=0x3f;
int head[N],num_edge;
struct Edge
{
    int to;
    int Next;
    long long dis;
}edge[N];
void add_edge(int from,int to,long long dis)
{
    edge[++num_edge].to=to;
    edge[num_edge].dis=dis;
    edge[num_edge].Next=head[from];
    head[from]=num_edge;
}
priority_queue<pair<long long,int> > Q;
long long d[N];
bool vis[N];
void Dijkstra(int S)
{
    memset(d,INF,sizeof(d));
    memset(vis,0,sizeof(vis));
    d[S]=1;
    Q.push(make_pair(0,S));
    while(!Q.empty())
    {
        int x=Q.top().second;
        Q.pop();
        if(vis[x])
            continue;
        vis[x]=1;
        for(int i=head[x];i;i=edge[i].Next)
        {
            int y=edge[i].to,z=edge[i].dis;
            if(d[y]>d[x]+z)
            {
                d[y]=d[x]+z;
                Q.push(make_pair(-d[y],y));
            }
        }
    }
}
int main()
{
    long long h;
    int x,y,z;
    cin>>h;
    cin>>x>>y>>z;
    if(x==1||y==1||z==1)
    {
        cout<<h<<endl;
        return 0;
    }
    for(int i=0;i<=x-1;i++)
    {
        add_edge(i,(i+z)%x,z);
        add_edge(i,(i+y)%x,y);
    }
    Dijkstra(1);
    long long ans=0;
    for(int i=0;i<=x-1;i++)
    {
        if(h>=d[i])
            ans=ans+((h-d[i])/x+1);
    }
    cout<<ans<<endl;
    return 0;
}

posted on 2020-11-09 21:30  DestinHistoire  阅读(93)  评论(0)    收藏  举报

导航