同余最短路
同余最短路
题型
- 给定 \(n\) 个整数,求这 \(n\) 个整数能拼凑出多少的其他整数( \(n\) 个整数可以重复取)
- 给定 \(n\) 个整数,求这 \(n\) 个整数不能拼凑出的最小(最大)的整数
- 至少要拼几次才能拼出模 \(k\) 余 \(p\) 的数
例题
P3403 跳楼机
题意:
给定 \(x,y,z\) ,问对于 \(k\in [1,n]\) 有多少个 \(k\) 能满足 \(ax+by+cz=k\) \((a,b,c\in N^+)\)
思路:
令 \(d(i)\) 表示仅通过操作 \(2,3\) \((a=0)\) 能达到的\(\mod x=i\) 的最小楼层
那么可得
\(d(i+y)=d(i)+y\) \(d(i+z)=d(i)+z\)
最短路的求法是:\(dis(x)=dis(y)+edge(x,y)\)
类比一下,让 \(i+y\) 和 \(i+z\) 变成点,\(y,z\) 变成边权,通过跑最短路求得 \(d(1\sim x-1)\)
因为 \(1\) 操作可以每次让楼层加 \(x\) ,所以最后的答案就是
\[\sum_{i=1}^{x-1} \frac{h-d(i)}{x} +1
\]
要 \(+1\) 因为算当前楼
注意尽量选较小的 \(x\) 当作模数,可以省时空复杂度
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
#define mp make_pair
#define pii pair <int,int>
#define int long long
const int N=1e5+5;
const int inf=2e18;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') f=-1;
ch=getchar();
}
while(isdigit(ch)){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
int h,x,y,z,ans;
int d[N];
bool vis[N];
vector <pii> G[N];
inline void add(int a,int b,int c){
G[a].pb(mp(b,c));
}
inline void SPFA(){
queue <int> q;
for(int i=0;i<N;++i) d[i]=inf;
d[1]=1,vis[1]=1;
q.push(1);
while(!q.empty()){
int u=q.front();q.pop();
vis[u]=0;
for(auto y:G[u]){
int v=y.first,val=y.second;
if(d[v]>d[u]+val){
d[v]=d[u]+val;
if(!vis[v]){
q.push(v);
vis[v]=1;
}
}
}
}
}
signed main(){
h=read(),x=read(),y=read(),z=read();
if(x>y) swap(x,y);
if(x>z) swap(x,z);
if(x==1||y==1||z==1){
cout<<h<<endl;
return 0;
}
for(int i=0;i<x;++i){
add(i,(i+z)%x,z);
add(i,(i+y)%x,y);
}
SPFA();
for(int i=0;i<x;++i) if(h>=d[i]) ans+=(h-d[i])/x+1;
cout<<ans;
}
P2371 墨墨的等式
题意:
给定 \(a[1\sim n]\) ,问对于 \(k\in [l,r]\) 有多少个 \(k\) 能满足 \(\sum_{i=1}^n a[i]\cdot x[i]\) \((x[1\sim n]\in N^+)\)
思路:
类似于跳楼机的做法
但是要算出 \(1\sim r\) 和 \(1\sim l-1\) 的答案,然后两个相减即为所求
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
#define mp make_pair
#define pii pair <int,int>
#define int long long
const int N=5e5+5;
const int inf=2e18;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') f=-1;
ch=getchar();
}
while(isdigit(ch)){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
int n,m,l,r,mn=inf;
int d[N],a[N];
bool vis[N];
vector <pii> G[N];
inline void add(int a,int b,int c){
G[a].pb(mp(b,c));
}
inline void SPFA(){
queue <int> q;
for(int i=0;i<N;++i) d[i]=inf;
d[0]=0,vis[0]=1;
q.push(0);
while(!q.empty()){
int u=q.front();q.pop();
vis[u]=0;
for(auto y:G[u]){
int v=y.first,val=y.second;
if(d[v]>d[u]+val){
d[v]=d[u]+val;
if(!vis[v]){
q.push(v);
vis[v]=1;
}
}
}
}
}
inline int query(int h){
int ans=0;
for(int i=0;i<mn;++i) if(h>=d[i]) ans+=(h-d[i])/mn+1;
return ans;
}
signed main(){
n=read(),l=read(),r=read();
for(int i=1;i<=n;++i){
int x=read();
if(x){
a[++m]=x;
mn=min(mn,x);
}
}
for(int i=0;i<mn;++i)
for(int j=1;j<=m;++j)
if(a[j]!=mn)
add(i,(i+a[j])%mn,a[j]);
SPFA();
cout<<query(r)-query(l-1)<<endl;
}