同余最短路
跳楼机
可以把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;
}