【洛谷P3403】跳楼机
这次是蓝题!
翻了OI wiki才发现还有个同余最短路这种东西
大为震撼 原来图论还有这种含义 人家本来以为只是在图上解决问题的说
看来还是要多逛OI wiki啊
当年我学OI的时候怎么没这玩意
我们先放题面,便于理解
P3403 跳楼机
题目背景
DJL 为了避免成为一只咸鱼,来找 srwudi 学习压代码的技巧。
题目描述
Srwudi 的家是一幢 \(h\) 层的摩天大楼。由于前来学习的蒟蒻越来越多,srwudi 改造了一个跳楼机,使得访客可以更方便的上楼。
经过改造,srwudi 的跳楼机可以采用以下四种方式移动:
- 向上移动 \(x\) 层;
- 向上移动 \(y\) 层;
- 向上移动 \(z\) 层;
- 回到第一层。
一个月黑风高的大中午,DJL 来到了 srwudi 的家,现在他在 srwudi 家的第一层,碰巧跳楼机也在第一层。DJL 想知道,他可以乘坐跳楼机前往的楼层数。
输入格式
第一行一个整数 \(h\),表示摩天大楼的层数。
第二行三个正整数,分别表示题目中的 \(x, y, z\)。
输出格式
一行一个整数,表示 DJL 可以到达的楼层数。
输入输出样例 #1
输入 #1
15
4 7 9
输出 #1
9
输入输出样例 #2
输入 #2
33333333333
99005 99002 100000
输出 #2
33302114671
说明/提示
可以到达的楼层有:\(1,5,8,9,10,12,13,14,15\)。
\(1 \le h \le 2^{63}-1\),\(1 \le x,y,z \le 10^5\)。
解法&&个人感想
其实,这道题是板子题
这是我第二次碰到蓝的板子题 上一次还是分层图
我们来介绍一下这个是什么东西
本题 你如果把h-- 起始楼层记为0的话
那么就是 ax+by+cz=k 这个方程有多少k($ k \le h$) 满足这个方程有非负整数解
为了优化复杂度 我们一般取x为三者中最小值
那么 我们考虑一个同余类 即 \(0 \le i \le x-1\)
这个同余类我们计d[i]为通过第二第三种操作能满足对i同余的最小值 从而从这个同余类出发 有$ {h-d[i]} \over x$ +1(包含d[i]本身)个可以满足条件的数
同时,由于同余类两两之间的交集为空,不需担心一个数被重复计算次数的事情
然后,我们建边的时候就按照add(i,(i+y)%x,y) 表示i->(i+y)%x这个同余类之间有一条长度为y的边
这样就可以求出类似最短路的东西了 起始点设为0 0到n的最短路径当然就是n的最小值
有点难理解对吧 确实是这样的
下面看代码:
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define maxm 200005
#define maxn 100005
using namespace std;
ll x,y,z;
ll head[maxm],nex[maxm],edge[maxm],ver[maxm];
ll h,tot;
ll a[4];
ll d[maxn];
ll vis[maxn];
ll ans=0;
const ll INF=(1ull)<<63-1;
void add(ll x,ll y,ll z){
ver[++tot]=y;
edge[tot]=z;
nex[tot]=head[x];
head[x]=tot;
}
void spfa(){
d[0]=0;
vis[0]=1;
queue<int>q;
q.push(0);
while(!q.empty()){
int x=q.front();q.pop();
vis[x]=0;
for(int i=head[x];i;i=nex[i]){
int y=ver[i],z=edge[i];
if(d[y]>d[x]+z){
d[y]=d[x]+z;
if(!vis[y]){
vis[y]=1;
q.push(y);
}
}
}
}
}
int main(){
scanf("%lld",&h);
scanf("%lld%lld%lld",&x,&y,&z);
if(x==1||y==1||z==1){
printf("%lld",h);
system("pause");
return 0;
}
h-=1;
for(int i=0;i<=x-1;i++){
add(i,(i+y)%x,y);
add(i,(i+z)%x,z);
d[i]=INF;
}
spfa();
for(int i=0;i<=x-1;i++){
if(h>=d[i]) ans+=(h-d[i])/x+1;
}
printf("%lld",ans);
system("pause");
return 0;
}
变式题型:洛谷P2731 墨墨的等式
晚上刚写完 代码放这:
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define maxm 6000006
#define maxn 500005
using namespace std;
int n,tot;
ll l,r;
int a[15];
ll ver[maxm],nex[maxm],head[maxm],edge[maxm];
int vis[maxn];
ll d[maxn];
const ll INF=(1ull)<<63-1;
void add(ll x,ll y,ll z){
ver[++tot]=y;
edge[tot]=z;
nex[tot]=head[x];
head[x]=tot;
}
void dijkstra(){
queue<int>q;
vis[0]=1;
d[0]=0;
q.push(0);
while(!q.empty()){
int x=q.front();q.pop();
vis[x]=0;
for(int i=head[x];i;i=nex[i]){
int y=ver[i],z=edge[i];
if(d[y]>d[x]+z){
d[y]=d[x]+z;
if(!vis[y]){
vis[y]=1;
q.push(y);
}
}
}
}
}
int main(){
scanf("%d%lld%lld",&n,&l,&r);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
if(n==1){
ll ans=0;
if(r>=a[1]) ans+=r/a[1];
if(l>=a[1]){
if(l%a[1]==0) ans-=l/a[1]-1;
else ans-=l/a[1];
}
printf("%lld",ans);
system("pause");
return 0;
}
int flag=0;
for(int i=1;i<=n;i++){
if(a[i]==1){
flag=1;
break;
}
}
if(flag==1){
printf("%lld",r-l+1);
system("pause");
return 0;
}
l-=1;
sort(a+1,a+1+n);
int res=0;
for(int i=1;i<=n;i++){
if(a[i]==0) continue;
else{
res=i;
break;
}
}
for(int i=0;i<=a[res]-1;i++){
for(int j=res+1;j<=n;j++){
add(i,(i+a[j])%a[res],a[j]);
d[(i+a[j])%a[res]]=INF;
}
}
dijkstra();
ll ans=0;
for(int i=0;i<=a[res]-1;i++){
if(r>=d[i]) ans+=(r-d[i])/a[res]+1;
if(l>=d[i]) ans-=(l-d[i])/a[res]+1;
}
printf("%lld",ans);
system("pause");
return 0;
}
解法&&个人感想
l-=1 化归为上题
本题需要特判 n=1 以及有零有一的情况
放张团子


浙公网安备 33010602011771号