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) 收藏 举报
浙公网安备 33010602011771号