最低可达层
之下的都不可达
之上的都可达
同余最短路,并不是用同余来跑最短路,而是通过同余来构造某些状态,从而达到优化时间空间复杂度的目的。往往这些状态就是最短路中的点,可以类比差分约束跑最短路((f[i]+w<=f[j])构造最短路不等式(例题luogu 小k的农场))
知识精要
https://oi-wiki.org/graph/mod-shortest-path/
https://www.cnblogs.com/chloris/p/11013913.html
跳楼机
最关键的
for(int i=0;i<x;i++)
add(i,(i+y)%x,y),
add(i,(i+z)%x,z);
spfa();
统计答案
for(int i=0;i<x;i++)
if(h>=dis[i])
ans+=(h-dis[i])/x+1;
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=2e5+10;
inline ll read() {
ll x=0;char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x;
}
ll h,dis[N],w[N];
int hd[N],to[N],nxt[N],tot;
inline void add(int x,int y,ll z) {
to[++tot]=y;w[tot]=z;nxt[tot]=hd[x];hd[x]=tot;
}
bool vis[N];
void spfa() {
dis[1]=1;
vis[1]=1;
queue<int>q;
q.push(1);
while(q.size()) {
int x=q.front();
q.pop();
vis[x]=0;
for(int i=hd[x];i;i=nxt[i]) {
int y=to[i];
if(dis[y]>dis[x]+w[i]) {
dis[y]=dis[x]+w[i];
if(!vis[y]) vis[y]=1,q.push(y);
}
}
}
}
int main() {
memset(dis,0x3f,sizeof(dis));
h=read();
int x,y,z,a[3];
a[0]=read();a[1]=read();a[2]=read();
sort(a,a+2);
x=a[0],y=a[1],z=a[2];
if(x==1||y==1||z==1) {
printf("%d
",h); return 0;
}
for(int i=0;i<x;i++)
add(i,(i+y)%x,y),
add(i,(i+z)%x,z);
spfa();
ll ans=0;
for(int i=0;i<x;i++)
if(h>=dis[i])
ans+=(h-dis[i])/x+1;
printf("%lld
",ans);
return 0;
}
墨墨的等式
和上面几乎一样
([l,r]) 套路——([1,r] - [1,l-1])
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=6000101;
inline ll read() {
ll x=0;char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x;
}
ll l,r,dis[N],w[N];
int hd[N],to[N],nxt[N],tot;
inline void add(int x,int y,ll z) {
to[++tot]=y;w[tot]=z;nxt[tot]=hd[x];hd[x]=tot;
}
bool vis[N];
void spfa() {
dis[0]=0;//
vis[0]=1;
queue<int>q;
q.push(0);
while(q.size()) {
int u=q.front();//不能用x,会出锅
q.pop();
vis[u]=0;
for(int i=hd[u];i;i=nxt[i]) {
int v=to[i];
if(dis[v]>dis[u]+w[i]) {
dis[v]=dis[u]+w[i];
if(!vis[v]) vis[v]=1,q.push(v);
}
}
}
}
int n,a[15];
int main() {
memset(dis,0x7f,sizeof(dis));
n=read();l=read();r=read();
for(int i=1;i<=n;i++) a[i]=read();
sort(a+1,a+n+1);
int x=a[1];
if(x==1) {
printf("%lld
",r-l+1); return 0;
}
for(int i=0;i<x;i++)
for(int j=2;j<=n;j++)
add(i,(i+a[j])%x,a[j]);
spfa();
ll ans=0;
l--;
for(int i=0;i<x;i++) {
if(r>=dis[i])
ans+=(r-dis[i])/x+1;
if(l>=dis[i])
ans-=((l-dis[i])/x+1);
}
printf("%lld
",ans);
return 0;
}
wait
牛场围栏
-
要求求出最大不能拼凑出来的木板长度,因此我们把最短的木板作为剩余系,扫描其他的木板并建边。题目另外说每个木板可以最多截掉m米,那么只要再扫描到每个木板的时候依次扫描这个木板能被截成的长度就好了。
-
如何解决不能凑出来的最大木板长度?我们求出的dist[]数组是不使用(a[1]-m)的长度外能凑出来的最小长度,那么只要从dist[]中减去这个值就能得出剩余系中这类不能拼凑的最大值。对所有类都进行这样的处理,最终就能得出不能凑出的最大值。
-
此外题目特判任意长度都可以拼成或最大值不存在输出"-1"。先考虑任意长度都能拼成,如果这个木板的长度能被削成1,那么一定可以达到任意长度,对应为
if(a[1]-m<=1)
。最大值不存在的情况为剩余系中一类数都无法拼成,即dist[k]没有被松弛。#include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int N=10005; const int M=60005; 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*10+ch-'0';ch=getchar();} return f*x; } int n,dis[N]; int hd[N],to[M],nxt[M],w[M],tot; inline void add(int x,int y,int z) { to[++tot]=y;w[tot]=z;nxt[tot]=hd[x];hd[x]=tot; } bool vis[N]; void spfa() { queue<int>q; memset(dis,0x3f,sizeof(dis)); q.push(0); dis[0]=0;vis[0]=1; while(q.size()) { int u=q.front();q.pop(); vis[u]=0;//开始写成了1、、、 for(int i=hd[u];i;i=nxt[i]) { int v=to[i]; if(dis[v]>dis[u]+w[i]) { dis[v]=dis[u]+w[i]; if(!vis[v]) vis[v]=1,q.push(v); } } } } int a[N],m,ans,x; int main() { n=read();m=read(); for(int i=1;i<=n;i++) a[i]=read(); sort(a+1,a+1+n); int x=a[1]-m; if(x<=1) {puts("-1");return 0;} for(int i=1;i<=n;i++) for(int j=max(a[i]-m,a[i-1]+1);j<=a[i];j++) for(int k=0;k<x;k++) add(k,(k+j)%x,j); spfa(); for(int i=0;i<x;i++) { if(dis[i]>=dis[N-1]) {puts("-1");return 0;} ans=max(ans,dis[i]-x); } printf("%d ",ans); return 0; }