【算法讲19:同余最短路】

  • 补充
  • 例题一
  • 思路一:动态规划
  • ⌈\lceil⌈同余最短路⌋\rfloor⌋
    • 核心代码 Accepted\color{green}AcceptedAccepted
  • 例题二
  • 例题三

补充

  • 同余最短路 是一个集 数学同余表达图论最短路 结合的算法。
    不是很难,需要尽早掌握。

例题一

  • 【链接】
    跳楼机 | 洛谷 P3403
  • 【题意】
    你一开始在第一层。一共有 hhh 层。
    你每一次操作,可以向上 xxx 层,或向上 yyy 层,或向上 zzz 层,或回到第一层。
    问你一共有多少层楼是你可以到达的
  • 【范围】
    1≤h≤263−11\le h\le 2^{63}-11≤h≤263−1
    1≤x,y,z≤1051\le x,y,z\le 10^51≤x,y,z≤105

思路一:动态规划

  • 设 dp[x]dp[x]dp[x] ,其中 dp[x]=1dp[x]=1dp[x]=1 表示能到达这一层楼,否则表示不能到达这一层楼。
    我们得到简单的状态转移方程:
    dp[t]=dp[t−x]∣dp[t−y]∣dp[t−z]dp[t]=dp[t-x]\ |\ dp[t-y] \ |\ dp[t-z] dp[t]=dp[t−x] ∣ dp[t−y] ∣ dp[t−z]
    简单的初始化:dp[1]=1,dp[t<1]=0dp[1]=1,dp[t<1]=0dp[1]=1,dp[t<1]=0,这里下标记作可以为负
  • 但是由于我们的 hhh 达到了很大的数量级,故这种做法时间复杂度为 O(h)O(h)O(h),只能得到 TLE\color{red}TLETLE

⌈\lceil⌈同余最短路⌋\rfloor⌋

  • 考虑到,如果我们通过某些操作到达了第 iii 层, 那么我们可以到达 i+axi+axi+ax 层,其中 a∈Na\in \mathbb{N}a∈N
    那么,如果我们能到达 iii 层,这一层及上面还可以到达的层数为 ⌊h−ix⌋+1\lfloor\dfrac{h-i}{x}\rfloor+1⌊xh−i​⌋+1 层。
    容易得到,如果能到达 i+axi+axi+ax 层,其中 a∈Na\in \mathbb{N}a∈N,那么必然能到达 iii 层。
    不妨,我们求出最小的一些 iii使得 iii 层为不用操作 xxx 就能到达的最小楼层。
    从同余的角度分析,即求出每一个 i∈[0,a)i\in[0,a)i∈[0,a),算出到达 t%a=it\%a=it%a=i 层的最小的 ttt
  • 我们设 dp[i]dp[i]dp[i] 表示 min⁡t{t%a=i满足t=by+cz,其中b,c∈N}\min_t\{t\%a=i\quad 满足\quad t=by+cz,其中 b,c\in \mathbb{N}\}mint​{t%a=i满足t=by+cz,其中b,c∈N}
    式子按照数学的角度设出来比较合理,但是做起来比较复杂。此时我们要按照图论的角度去分析了。
    如果我们能到达 iii 层,那么我们还能到达 i+yi+yi+y 层和 i+zi+zi+z 层,当然前提是层数 ≤h\le h≤h
    按照 dpdpdp 式子建图,即 i→边权为y(i+y)%ai\overset{边权为y}{\rightarrow}(i+y)\%ai→边权为y(i+y)%a 和 i→边权为z(i+z)%ai\overset{边权为z}{\rightarrow}(i+z)\%ai→边权为z(i+z)%a
    容易得到这就是跑一个最短路。点数和边数都是 1e51e51e5 级别的,用 dijkstradijkstradijkstra 即可。

核心代码 Accepted\color{green}AcceptedAccepted

  • 时间复杂度:O(alog⁡a)O(a\log a)O(aloga)
/*_            __   __          _          _
| |           \ \ / /         | |        (_)
| |__  _   _   \ V /__ _ _ __ | |     ___ _
| '_ \| | | |   \ // _` | '_ \| |    / _ \ |
| |_) | |_| |   | | (_| | | | | |___|  __/ |
|_.__/ \__, |   \_/\__,_|_| |_\_____/\___|_|__/ ||___/
*/
ll h;int a,b,c;
ll dp[MAX];
vector<int>V[MAX],G[MAX];
priority_queue<pair<ll,ll> >Q;void dijkstra(int s){Q.push(make_pair(-1,s));while(!Q.empty()){int x = Q.top().second;ll t = -Q.top().first;Q.pop();if(t > dp[x])continue;for(auto it : V[x]){if(dp[it] > dp[x] + a && dp[x] + a <= h){dp[it] = dp[x] + a;Q.push(make_pair(-dp[it],it));}}for(auto it : G[x]){if(dp[it] > dp[x] + b && dp[x] + b <= h){dp[it] = dp[x] + b;Q.push(make_pair(-dp[it],it));}}}
}int main()
{cin >> h >> a >> b >> c;for(int i = 0;i < c;++i)dp[i] = LINF;dp[1] = 1;for(int i = 0;i <= c;++i){V[i].push_back((i+a)%c);G[i].push_back((i+b)%c);}dijkstra(1);ll res = 0;for(int i = 0;i < c;++i){if(dp[i] == LINF)continue;res = res + ((h - dp[i]) / c) + 1;}cout << res;return 0;
}

例题二

  • 【链接】墨墨的等式 | 洛谷 P2371
  • 【题意】
    给定 n,a1,⋯,an,l,rn,a_1,\cdots,a_n,l,rn,a1​,⋯,an​,l,r
    问你有多少组非负解向量 x1,⋯,xnx_1,\cdots,x_nx1​,⋯,xn​,满足:
    ∑i=1naixi∈[l,r]\sum_{i=1}^na_ix_i\in[l,r] i=1∑n​ai​xi​∈[l,r]
  • 【范围】
    n≤12n\le 12n≤12
    0≤ai≤5×1050\le a_i\le 5\times 10^50≤ai​≤5×105
    1≤l≤r≤10121\le l\le r\le 10^{12}1≤l≤r≤1012
  • 【思路】
    首先和第一题的跳楼机非常非常像。跳楼机的每种操作次数相当于这里的解向量的元素xix_ixi​的值。
    为了方便,设 dp[i]dp[i]dp[i] 表示 min⁡h{h%a1=i并且h可以到达}\min_h\{h\%a_1=i\quad并且\quad h可以到达\}minh​{h%a1​=i并且h可以到达}
    然后正常地去建图貌似就没区别了。
    不过它让你求 [l,r][l,r][l,r] 范围内解的数量,我们只要求出 [1,r][1,r][1,r] 数量减去 [1,l−1][1,l-1][1,l−1] 数量即可。
  • 【代码】Accepted\color{green}AcceptedAccepted
    时间复杂度:O(n×a1log⁡a1)O(n\times a_1\log a_1)O(n×a1​loga1​)
/*_            __   __          _          _
| |           \ \ / /         | |        (_)
| |__  _   _   \ V /__ _ _ __ | |     ___ _
| '_ \| | | |   \ // _` | '_ \| |    / _ \ |
| |_) | |_| |   | | (_| | | | | |___|  __/ |
|_.__/ \__, |   \_/\__,_|_| |_\_____/\___|_|__/ ||___/
*/
int n;
int aa[20];
ll dp[MAX];
ll V[MAX][17];
priority_queue<pair<ll,ll> >Q;void dijkstra(int s,ll h){Q.push(make_pair(0,s));while(!Q.empty()){int x = Q.top().second;ll t = -Q.top().first;Q.pop();if(t > dp[x])continue;for(int i = 2;i <= n;++i){int it = V[x][i];if(dp[it] > dp[x] + aa[i] && dp[x] + aa[i] <= h){dp[it] = dp[x] + aa[i];Q.push(make_pair(-dp[it],it));}}}
}
ll solve(ll h){for(int i = 0;i < aa[1];++i)dp[i] = LINF;dp[0] = 0;for(int i = 0;i < aa[1];++i){for(int j = 2;j <= n;++j){V[i][j] = (i+aa[j]) % aa[1];}}dijkstra(0,h);ll res = 0;for(int i = 0;i < aa[1];++i){if(dp[i] == LINF)continue;res = res + ((h - dp[i]) / aa[1]) + 1;}return res;
}
int main()
{ll l,r;cin >> n >> l >> r;for(int i = 1;i <= n;++i)cin >> aa[i];ll res = solve(r) - solve(l-1);cout << res;return 0;
}

例题三

  • 【链接】
    Lazy Running | HDU 6071
  • 【题意】
    有四个点。给出 d1,2d_{1,2}d1,2​,d2,3d_{2,3}d2,3​,d3,4d_{3,4}d3,4​,d4,1d_{4,1}d4,1​,表示两个点之间的无向边的长度。
    问你,从点二出发到点二结束,路径距离和 ≥K\ge K≥K 的最小值为多少。
  • 【范围】
    1≤d≤3×1041\le d\le 3\times 10^41≤d≤3×104
    1≤K≤10181\le K\le 10^{18}1≤K≤1018
  • 【思路】
    设 w=min⁡(d1,2,d2,3)w=\min(d_{1,2},d_{2,3})w=min(d1,2​,d2,3​)。
    如果你跑到 222 号点已经走了 xxx 距离的话,你可以通过来回跳跃,每次增加 2w2w2w 的方式到达 222 号点。
    于是,此时最短路长度为:x+2wt≥Kx+2wt\ge Kx+2wt≥K,满足 t≥0t\ge0t≥0
    就跟跳楼机类似。
    我们只需要求出i∈[0,2w)i\in[0,2w)i∈[0,2w),到每个节点 vvv 的路径长模 iii 的最小值 dp[x][i]dp[x][i]dp[x][i] 即可。
    然后我们需要求得所有的 dp[2][i]dp[2][i]dp[2][i],然后求出最小的 t≥0t\ge0t≥0 满足 x+2wt≥Kx+2wt\ge Kx+2wt≥K
    那么答案即为 min⁡{x+2wt}\min\{x+2wt\}min{x+2wt}
  • 一些小细节:
    虽然我们对于每一个点都有 2w2w2w 个状态,那么我们优先队列是不是需要存 {x,i,dp[x][i]}\{x,i,dp[x][i]\}{x,i,dp[x][i]} 呢?这里不需要,因为根据定义得到 dp[x][i]%2w=idp[x][i]\%2w=idp[x][i]%2w=i,我们少存一维状态,可以节省许多时间和内存,防止代码超时。
  • 【代码】Accepted\color{green}AcceptedAccepted
    时间复杂度:O(dlog⁡d)O(d\log d)O(dlogd)
/*_            __   __          _          _
| |           \ \ / /         | |        (_)
| |__  _   _   \ V /__ _ _ __ | |     ___ _
| '_ \| | | |   \ // _` | '_ \| |    / _ \ |
| |_) | |_| |   | | (_| | | | | |___|  __/ |
|_.__/ \__, |   \_/\__,_|_| |_\_____/\___|_|__/ ||___/
*/
ll K;
int aa[5][5];
ll dp[10][MAX];
struct node{int x;ll shu;bool operator < (const node &ND)const{return shu > ND.shu;}
};
priority_queue<node>Q;vector<pair<int,int> >V[5];void dijkstra(int s,ll w){Q.push({s,0});while(!Q.empty()){int x = Q.top().x;int y = Q.top().shu % w;ll t = Q.top().shu;Q.pop();if(t > dp[x][y])continue;for(auto it : V[x]){int to = it.first;int  d = it.second;if(dp[to][(t + d) % w] > t + d){dp[to][(t + d) % w] = t + d;Q.push({to,dp[to][(t + d) % w]});}}}
}
ll solve(){ll w = min(aa[1][2],aa[2][3]);w = w * 2;if(w >= K)return w;for(int i = 1;i <= 4;++i)for(int j = 0;j < w;++j)dp[i][j] = LINF;dp[2][0] = 0;dijkstra(2,w);ll res = LINF;for(int i = 0;i < w;++i){if(dp[2][i] == LINF)continue;if(dp[2][i] >= K){              /// 我们不能允许 t 为负数res = min(res,dp[2][i]);continue;}                             /// 简单使用 floor 函数会导致精度出现错误res = min(res,dp[2][i] + w * (((K - dp[2][i]) / w) + ((K - dp[2][i]) % w != 0 ? 1LL : 0LL)));}return res;
}
int main()
{int T;cin >> T;while(T--){cin >> K >> aa[1][2] >> aa[2][3] >> aa[3][4] >> aa[4][1];aa[2][1] = aa[1][2];aa[3][2] = aa[2][3];aa[4][3] = aa[3][4];aa[1][4] = aa[4][1];for(int i = 1;i <= 4;++i)V[i].clear();for(int i = 1;i <= 4;++i){int to = i % 4 + 1;V[i].push_back(make_pair(to,aa[i][to]));V[to].push_back(make_pair(i,aa[i][to]));}cout << solve() << endl;}return 0;
}

【算法讲19:同余最短路】跳楼机 | 墨墨的等式 | Lazy Running相关推荐

  1. 【NOIP模拟题】【DP】【同余最短路】【暴力剪枝】2016.11.15 第二题 小L的牛栏 题解

    小L的牛栏 [题目描述] 小L通过泥萌的帮助,成功解决了二叉树的修改问题,并因此写了一篇论文, 成功报送了叉院(羡慕不?).勤奋又勤思的他在研究生时期成功转系,考入了北京大学光华管理学院!毕业后,凭着 ...

  2. 【算法笔记】一步一步推出来的同余最短路优化思路(千字长文,超详细)

    整理的算法模板合集: ACM模板 目录 同余最短路 例题1:luogu P3403 跳楼机 例题2:luogu P2371 [国家集训队]墨墨的等式 例题3:luogu P2662 牛场围栏 同余最短 ...

  3. 同余最短路(P3403 跳楼机)

    同余最短路 前置 给定m个数,这m个数可以重复取,问最大的这m个数不能拼成的数,或者给定一定范围,范围里有多少个数是这m个数可以拼成的,对于这种问题我们可以考虑同余最短路的算法. P3403 跳楼机 ...

  4. P2662 牛场围栏(同余最短路)

    P2662 牛场围栏 思路 假设我们已经知道同余最短路是什么了,这里就不再过多赘述. 我们要尽可能地得到更多地课建成地边,那么我们必然要选一个basebasebase相对小的,因此我们可以对所有的棍子 ...

  5. 【学习笔记】同余最短路

    同余最短路是用来解决一类 ∑i=1naixi∈[L,R]\sum_{i=1}^n a_ix_i\in[L,R]∑i=1n​ai​xi​∈[L,R] 问题的方法. 其中 L,RL,RL,R 值非常大,而 ...

  6. 【算法讲18:二次剩余】勒让德符号 | 欧拉判别法 | Cipolla 算法

    [算法讲18:二次剩余] Source\mathfrak{Source}Source ⌈\lceil⌈二次剩余⌋\rfloor⌋与⌈\lceil⌈二次非剩余⌋\rfloor⌋ ⌈\lceil⌈二次互反 ...

  7. HMM 前向 后向 Viterbi算法讲解通透的

    HMM  前向 后向 Viterbi算法讲解通透的 https://blog.csdn.net/xueyingxue001/article/details/52396494 什么是HMM https: ...

  8. 牛客 - Yuki with emofunc and playf(同余最短路)

    题目链接:点击查看 题目大意:初始时给出一个数字 y=1y=1y=1 和一个辅助数字 xxx,每回合可以执行两种操作中的一种: y=y∗10y=y*10y=y∗10 y=y+x−1y=y+x-1y=y ...

  9. HDU 6071 Lazy Running(同余最短路的应用)

    Lazy Running 思路 还是利用同余的思想,假设存在一条长度为kkk的路,那么也一定存在一条k+basek + basek+base的路base=2∗min(d1,d2)base = 2 * ...

  10. 湖南大学第十六届程序设计竞赛 B Yuki with emofunc and playf 同余最短路

    传送门 文章目录 题意: 思路: 题意: 初始有一个数111,你每次可以将其∗10*10∗10或者+(x−1)+(x-1)+(x−1),现在给你xxx,问最少经过多少步能到达nnn. 1≤n≤1e6, ...

最新文章

  1. java的不足_Java不足之我见
  2. express运行原理
  3. 设计模式(19)-Observer Pattern
  4. rocketmq之控制台rocketmq-console编译安装(二)
  5. keil5建立多文件的时候为什么总是出错
  6. 年月跨度_不畏困难,砥砺前行 ——国内最大跨度管桁架工程成功首滑
  7. 关于生活,可能有用的40条建议
  8. 一篇彻底搞懂jsp内置对象
  9. 我的小导师-天津大学张梅山老师要招NLP方向的研究生啦!
  10. 数据结构之二叉查找树介绍
  11. 计算机 映射网络驱动器,win7电脑映射网络驱动器的方法?
  12. python曼·惠特尼U检验(Mann-Whitney U Test)
  13. NetApp存储常用检查命令
  14. 利用按键精灵实现QQ群发和微信群发
  15. 电子商务安全体系架构
  16. 黑苹果alc269声卡仿冒id_10.10中我的ALC269VC依旧无声?学习并尝试制作了仿冒声卡,依然无声。...
  17. python自动安装windows软件_python模块 - pywinauto(windows自动化安装软件)
  18. vmware esxi 6-7 全套资源分享(定期更新)
  19. iOS14桌面伪透明
  20. 淘宝开源代码检测工具!(附源码)

热门文章

  1. 将路由器作为AP来使用
  2. (七)线程的优先级Priority和关键词:synchronized
  3. 搜狗 语音输入法 linux,搜狗输入法Linux1.0企业版发布 全面提升用户输入体验
  4. 计算机网络连接图标在哪,电脑网络连接图标不见了
  5. 面对突如其来的新冠,“AI”可以做点什么?
  6. Pyhton 兔子繁衍问题(后有递归求法)(设有一对新生的兔子从第4个月开始他们每个月月初都生一对新兔子, 新生的兔子从第4个月开始又每个月月初生一对兔子,求n个月后兔子的总对数)
  7. 自我鉴定计算机专业大学,大学生计算机专业的自我鉴定书
  8. 项目经理应该知道的三种项目管理技术
  9. Win10更新失败的解决办法
  10. golang 结构体使用chan