同余最短路(总结)
Posted Harris-H
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了同余最短路(总结)相关的知识,希望对你有一定的参考价值。
同余最短路(总结)
同余最短路其实是一种优化最短路建图的方法。
通常是解决给定m个整数,求这m个整数能拼凑出多少的其他整数(这m个整数可以重复取)或给定m个整数,求这m个整数不能拼凑出的最小(最大)的整数。
时间复杂度: O ( m l o g n ) O(mlogn) O(mlogn)
P3403 跳楼机
给定 x , y , z , h x,y,z,h x,y,z,h
a x + b y + c z = k ax+by+cz=k ax+by+cz=k, a , b , c ≥ 0 a,b,c\\ge 0 a,b,c≥0,求满足条件 k ≤ h k\\le h k≤h的 k k k有多少个。
考虑令 d i d_i di表示只通过 y , z y,z y,z可以得到的最小值,且 d i ( m o d x ) = i d_i\\pmod {x}=i di(modx)=i。
从第 d i d_i di层开始,我们可以通过 x x x来增加贡献。
即: d i + x , d i + 2 x , d i + 3 x … … d_i+x,d_i+2x,d_i+3x\\dots\\dots di+x,di+2x,di+3x……
因此这部分的贡献是: h − d i x \\dfrac{h-d_i}{x} xh−di
而且加上 d i d_i di 这层的贡献,就是 h − d i x + 1 \\dfrac{h-d_i}{x}+1 xh−di+1。
因此最后的答案就是: ∑ i = 0 x − 1 ( h − d i x + 1 ) \\sum\\limits_{i=0}^{x-1}(\\dfrac{h-d_i}{x}+1) i=0∑x−1(xh−di+1)
注意需要满足 h ≥ d i h\\ge d_i h≥di 才会产生贡献。
可能有人会有疑惑,那 ( 1 , x ) (1,x) (1,x)之间的数会不会漏掉呢?答案是不会的。
如果通过 y , z y,z y,z可以得到 ( 1 , x ) (1,x) (1,x)之间的数,则这个贡献会被算到 1 1 1里去。
如果通过 y , z y,z y,z不能得到 ( 1 , x ) (1,x) (1,x)之间的数,那么显然 x , y , z x,y,z x,y,z组合更得到不到,因为 x > ( 1 , x ) x>(1,x) x>(1,x)。
注意 x , y , z x,y,z x,y,z有等于1时,需要特判。
// Problem: P3403 跳楼机
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3403
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// Date: 2021-07-08 18:22:37
// --------by Herio--------
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=1e5+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb emplace_back
#define SZ(a) (int)a.size()
#define ios ios::sync_with_stdio(false),cin.tie(0)
void Print(int *a,int n){
for(int i=1;i<n;i++)
printf("%d ",a[i]);
printf("%d\\n",a[n]);
}
ll H;
int x,y,z;
ll d[N];
struct edge{
int to,nt,w;
}e[N<<1];
int h[N],cnt;
void add(int u,int v,int w){
e[++cnt]={v,h[u],w};h[u]=cnt;
}
int vis[N];
void spfa(){
queue<int>q;
mst(d,0x3f);
vis[1]=d[1]=1;
q.push(1);
while(!q.empty()){
int u=q.front();q.pop();vis[u]=0;
for(int i=h[u];i;i=e[i].nt){
int v=e[i].to,w=e[i].w;
if(d[v]>d[u]+w){
d[v]=d[u]+w;
if(!vis[v]) q.push(v),vis[v]=1;
}
}
}
}
int main(){
scanf("%lld%d%d%d",&H,&x,&y,&z);
if(x==1||y==1||z==1) {
printf("%lld\\n",H);return 0;
}
for(int i=0;i<x;i++) add(i,(i+y)%x,y),add(i,(i+z)%x,z);
spfa();
ll s=0;
for(int i=0;i<x;i++)
if(d[i]<=H){
s+=(H-d[i])/x+1;
}
printf("%lld\\n",s);
return 0;
}
P2371 [国家集训队]墨墨的等式
一样的题,只不过三种方式变成了 n n n种方式,而且做了个前缀和处理。
先排序,然后用最小的值作为余数,跑最短路。
注意当有 a i = 1 a_i=1 ai=1直接输出, r − l + 1 r-l+1 r−l+1。
有 0 0 0直接跳过,避免重复加边。
跑 0 0 0开始的最短路。
// Problem: P3403 跳楼机
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3403
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// Date: 2021-07-08 18:22:37
// --------by Herio--------
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=6e6+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb emplace_back
#define SZ(a) (int)a.size()
#define IOS ios::sync_with_stdio(false),cin.tie(0)
void Print(int *a,int n){
for(int i=1;i<n;i++)
printf("%d ",a[i]);
printf("%d\\n",a[n]);
}
ll d[N];
struct edge{
int to,nt,w;
}e[N<<1];
int h[N],cnt;
void add(int u,int v,int w){
e[++cnt]={v,h[u],w};h[u]=cnt;
}
int vis[N];
void spfa(){
queue<int>q;
mst(d,0x3f);
vis[0]=d[0]=0;
q.push(0);
while(!q.empty()){
「学习笔记」同余最短路