接触到的新内容,【同余最短路】。

代码很好写,但思路不好理解。

同余最短路,并不是用同余来跑最短路,而是通过同余来构造某些状态,从而达到优化时间空间复杂度的目的。往往这些状态就是最短路中的点,可以类比差分约束跑最短路(f[i]+w<=f[j]构造最短路不等式(例题luogu 小k的农场))

——来自洛谷P3403 Liao_rl 的题解

【P3403跳楼机】

题目背景

DJL为了避免成为一只咸鱼,来找srwudi学习压代码的技巧。

题目描述

Srwudi的家是一幢h层的摩天大楼。由于前来学习的蒟蒻越来越多,srwudi改造了一个跳楼机,使得访客可以更方便的上楼。

经过改造,srwudi的跳楼机可以采用以下四种方式移动:

  1. 向上移动x层;

  2. 向上移动y层;

  3. 向上移动z层;

  4. 回到第一层。

一个月黑风高的大中午,DJL来到了srwudi的家,现在他在srwudi家的第一层,碰巧跳楼机也在第一层。DJL想知道,他可以乘坐跳楼机前往的楼层数。

输入输出格式

输入格式:

第一行一个整数h,表示摩天大楼的层数。

第二行三个正整数,分别表示题目中的x, y, z。

输出格式:

一行一个整数,表示DJL可以到达的楼层数。

输入输出样例

输入样例#1:

  1. 15
  2. 4 7 9
输出样例#1:

  1. 9
输入样例#2:

  1. 33333333333
  2. 99005 99002 100000
输出样例#2:

  1. 33302114671

说明

可以到达的楼层有:1,5,8,9,10,12,13,14,15

想不出来不要死磕这一题,先看看第三题。。。。

1<=h<=2^63-1

1<=x, y, z<=100000

思路

首先考虑低一级的问题,假如只存在x,y两种操作而不是x,y,z三种(暂时忽略回到第一层)

那么很好想,只要用y能凑出来x的剩余系(%x的所有余数的集合),那么所有楼层都能达到

设i为%x的一个余数,f[i]为用y能凑出来的%x=i的最小高度,那么f[i]再往上跳任意个x的层数都能达到,即对于f[x]能达到的层数为(h-f[i])/x+1。因为程序里面的除法是向下取整,后面还要补一个1。另外,由于f数组的存在,一定不存在整除的情况。

那么把所有这样的f[i]加起来就是所求的答案。推广到三种操作的情况,只要用y和z去凑f就能得到答案。

接下来问题是怎么求f[i]。假设我们已经求出一个f[i],在它的基础上再执行一遍y或z操作,例如用f[i]+y,显然能得到f[i+y]。那么考虑让f[i+y]和f[i]都成为点,用y作为边相连,以已知状态推出其它所求状态。因为f数组范围是x的剩余系,为了使f最小我们选择x,y,z中最小的成为x,连边时f[i+y]要作为f[(i+y)%x]这个点使用,对结果并无影响。

考虑起始状态是第一层且最低只能回到第一层,那么已知状态是f[1]=1。用SPFA来松弛这些等式关系,跑出f数组。最后用h计算总答案的时候,要排除掉f[i]中>h的答案。

这道题中的同余最短路思想,个人理解在于利用同余来构造x的剩余系这些点。f[i],f[i]+x,f[i]+2x,...,f[i]+kx,这些楼层数都%x同余,那么只要建立f[i]的状态就能用极低的时间复杂度求出f[i]+x,f[i]+2x等是否可行。

代码:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<queue>
  5. using namespace std;
  6. long long h,ans,f[];
  7. long long x,y,z,vis[];
  8. queue<long long>q;
  9. long long ver[],head[],edge[],Next[],tot;
  10. void add(long long x,long long y,long long z){
  11. ver[++tot]=y;
  12. Next[tot]=head[x];
  13. edge[tot]=z;
  14. head[x]=tot;
  15. }
  16. int main()
  17. {
  18. // freopen("srwudi2.in","r",stdin);
  19. // freopen("srwudi2.out","w",stdout);
  20. scanf("%lld%lld%lld%lld",&h,&x,&y,&z);
  21. if(x==||y==||z==){
  22. printf("%lld",h);
  23. return ;
  24. }
  25. memset(f,0x7f,sizeof(f));
  26. f[]=;
  27. vis[]=;
  28. if(y<z)swap(y,z);
  29. if(x<z)swap(x,z);
  30. for(int i=;i<z;i++)add(i,(i+x)%z,x),add(i,(i+y)%z,y);
  31. q.push();
  32. while(!q.empty()){
  33. long long u=q.front();
  34. q.pop();
  35. vis[u]=;
  36. for(int i=head[u];i;i=Next[i]){
  37. long long v=ver[i],zz=edge[i];
  38. if(f[v]>f[u]+zz){
  39. f[v]=f[u]+zz;
  40. if(!vis[v]){
  41. vis[v]=;
  42. q.push(v);
  43. }
  44. }
  45. }
  46. }
  47. for(int i=;i<z;i++){
  48. if(f[i]<=h)ans+=(h-f[i])/z+;
  49. }
  50. printf("%lld",ans);
  51. }

那么再来看一道相似的题

【P2371[国家集训队]墨墨的等式】

题目描述

墨墨突然对等式很感兴趣,他正在研究a1​x1​+a2​x2​+…+an​xn​=B存在非负整数解的条件,他要求你编写一个程序,给定N、{an}、以及B的取值范围,求出有多少B可以使等式存在非负整数解。

输入输出格式

输入格式:

输入的第一行包含3个正整数,分别表示N、BMin​、BMax​分别表示数列的长度、B的下界、B的上界。

输入的第二行包含N个整数,即数列{an}的值。

输出格式:

输出一个整数,表示有多少b可以使等式存在非负整数解。

输入输出样例

输入样例#1:

  1. 2 5 10
  2. 3 5
输出样例#1:

  1. 5

说明

对于20%的数据,N≤5,1≤BMin​≤BMax​≤10。

对于40%的数据,N≤10,1≤BMin​≤BMax​≤10^6。

对于100%的数据,N≤12,0≤ai​≤5∗10^5,1≤BMin​≤BMax​≤10^12。

思路

这道题一眼看上去就和上面那道很像对吧XD

但是这道题不仅限于x,y,z而是拓展到了n个数字。不过这并不影响,可以如法炮制,把最小的x选出来,构造它的剩余系对应状态,只是要建很多点了而已。

还有一个不同的地方是这题的询问是有范围的,而不是1-h,所以在询问的时候可以利用很多题目里常见的前缀和思想,即query(max)-query(min-1),来得出答案。

代码:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<queue>
  5. using namespace std;
  6. long long n,a[],x=,vis[];
  7. long long f[];
  8. long long tot,ver[],Next[],head[],edge[];
  9. long long maxx,minn,ans;
  10. queue<long long>q;
  11. void add(long long x,long long y,long long z){
  12. ver[++tot]=y;
  13. Next[tot]=head[x];
  14. edge[tot]=z;
  15. head[x]=tot;
  16. }
  17. int main()
  18. {
  19. scanf("%lld%lld%lld",&n,&minn,&maxx);
  20. memset(f,0x7f,sizeof(f));
  21. for(int i=;i<=n;i++){
  22. scanf("%lld",&a[i]);
  23. if(a[i]==){
  24. printf("%lld",maxx-minn+);
  25. return ;
  26. }
  27. x=min(a[i],x);
  28. }
  29. for(int i=;i<=n;i++){
  30. if(a[i]==x)continue;
  31. for(int j=;j<x;j++){
  32. add(j,(j+a[i])%x,a[i]);
  33. }
  34. }
  35. f[]=,vis[]=;
  36. q.push();
  37. while(!q.empty()){
  38. int u=q.front();
  39. q.pop();
  40. vis[u]=;
  41. for(int i=head[u];i;i=Next[i]){
  42. long long v=ver[i],z=edge[i];
  43. if(f[v]>f[u]+z){
  44. f[v]=f[u]+z;
  45. if(!vis[v]){
  46. vis[v]=;
  47. q.push(v);
  48. }
  49. }
  50. }
  51. }
  52. minn-=;
  53. for(int i=;i<x;i++){
  54. if(f[i]<=maxx){
  55. ans+=(maxx-f[i])/x+;
  56. }
  57. if(f[i]<=minn){
  58. ans-=((minn-f[i])/x+);
  59. }
  60. }
  61. printf("%lld",ans);
  62. return ;
  63. }

(来自一个WA多了气急败坏的无脑long long选手)

【同余最短路】【例题集合】洛谷P3403 跳楼机/P2371 墨墨的等式的更多相关文章

  1. 洛谷P3403跳楼机(最短路构造/同余最短路)

    题目-> 解题思路: 最短路构造很神啊. 先用前两个值跑在第三个值模意义下的同余最短路(这步贪心可以证明,如果第三步长为z,那么如果n+z可以达到,n+2z同样可以达到) 最后计算与楼顶差多少个 ...

  2. [洛谷P3403] 跳楼机

    题目传送门 套路题,同余最短路. 先只考虑y.z进行连边,再在mod x的意义下进行计算. 这里的“距离”dis[i]指的是,在所有满足a mod x=i的a里,能到达的最小的a是多少. 显然只要能到 ...

  3. luogu P3403 跳楼机 同余最短路

    LINK:跳楼机 很早之前就想学的一个东西.发现这个东西果然神奇. 我们要找到 所有的 w满足 \(w=1+ax+by+cz\).且 \(1\leq w\leq h\) 暴力枚举是不行的. 做法是这样 ...

  4. k短路模板(洛谷P2483 [SDOI2010]魔法猪学院)(k短路,最短路,左偏树,priority_queue)

    你谷数据够强了,以前的A*应该差不多死掉了. 所以,小伙伴们快来一起把YL顶上去把!戳这里! 俞鼎力的课件 需要掌握的内容: Dijkstra构建最短路径树. 可持久化堆(使用左偏树,因其有二叉树结构 ...

  5. 状压DP概念 及例题(洛谷 P1896 互不侵犯)

    状压DP 就是状态压缩DP.所谓状态压缩,就是将一些复杂的状态压缩起来,一般来说是压缩为一个二进制数,用01来表示某一元素的状态. 比如一排灯泡(5个) 我们可以用一串二进制01串来表示他们的状态 1 ...

  6. Solution -「CERC 2016」「洛谷 P3684」机棚障碍

    \(\mathcal{Description}\)   Link.   给一个 \(n\times n\) 的网格图,每个点是空格或障碍.\(q\) 次询问,每次给定两个坐标 \((r_1,c_1), ...

  7. 【同余最短路】洛谷 P2662 牛场围栏

    关于同余最短路的部分 [同余最短路]P3403跳楼机/P2371墨墨的等式 [P2662牛场围栏] 题目背景 小L通过泥萌的帮助,成功解决了二叉树的修改问题,并因此写了一篇论文, 成功报送了叉院(羡慕 ...

  8. FFT/NTT总结+洛谷P3803 【模板】多项式乘法(FFT)(FFT/NTT)

    前言 众所周知,这两个东西都是用来算多项式乘法的. 对于这种常人思维难以理解的东西,就少些理解,多背板子吧! 因此只总结一下思路和代码,什么概念和推式子就靠巨佬们吧 推荐自为风月马前卒巨佬的概念和定理 ...

  9. 分数规划模板(洛谷P4377 [USACO18OPEN]Talent Show)(分数规划,二分答案,背包)

    分数规划是这样一个东西: 给定若干元素,每个元素有两个属性值\(a_i,b_i\),在满足题目要求的某些限制下选择若干元素并求出\(\frac{\sum a}{\sum b}\)的最大值. 如果没有限 ...

随机推荐

  1. QQ交流群

  2. C++ 变量、常量、符号常量

    变量: int i = 0;  // i 是变量 i=5;         //i 可以修改 变量就是程序内一个内存位置的符号名,在该内存位置可以保存数据,并可以通过符号名对该内存地址存放的数据进行访 ...

  3. HTML 项目符号

    无序符号 <ul> <li> </li> <li> </li> <li> </li> </ul> 属性 ...

  4. NEO4J的安装配置及使用总结

    #工具:使用neo4j desktop版本# 一,下载工具 可以到官方网站上下载桌面版或者community版本的,下载地址:https://neo4j.com/, 安装好. 二.配置环境变量 本文参 ...

  5. 2018-8-10-WPF-鼠标移动到列表上-显示列表图标

    title author date CreateTime categories WPF 鼠标移动到列表上 显示列表图标 lindexi 2018-08-10 19:16:51 +0800 2018-2 ...

  6. Windows API 第17篇 GetLogicalDriveStrings 获取本机所有逻辑驱动器,以根目录的形式表示

    函数原型:DWORD GetLogicalDriveStrings(  DWORD nBufferLength,  // size of buffer                          ...

  7. HZOI20190813 B,任(duty)

    题面:去一个神奇的网页:https://www.cnblogs.com/Juve/articles/11352426.html 听说打O(nmq)有70 但是显然博主只有50分 考点:前缀和的综合应用 ...

  8. day26 作业

    目录 TCP三次握手.四次挥手图 三次握手 四次挥手 简明理解三次握手 基于TCP开发一款远程CMD程序 TCP三次握手.四次挥手图 三次握手 第一次握手:客户端给服务端发一个 SYN 报文,并指明客 ...

  9. SQL Server作业的备份

    作业备份,不是备份数据库,是备份作业.我的方法是把作业导出成文件备份起来,因为当你服务器维护的多了的时候很多你的作业 就很成问题,很麻烦.最好能够作业实现同步,这个也是第一步,保存成文件,之后个人设想 ...

  10. mysql中的字符集和校对规则(mysql校对集)

    1.简要说明介绍 字符集和校对规则 字符集是一套符号和编码.校对规则是在字符集内用于比较字符的一套规则. MySql在collation提供较强的支持,oracel在这方面没查到相应的资料. 不同字符 ...