同余最短路经典问题总结

  • 同余最短路经典套路
  • P3403
  • P2371
  • P2662
  • 牛客练习赛60:D

同余最短路经典套路

给定nnn个数,每个数都可以选多次(也可以不选),将所有选中的数相加。问最终能够组成多少种不同的数或者不能组成的最大数或者。。。
形如ax+by+cz+..=kax+by+cz+..=kax+by+cz+..=k,其中a,b,ca,b,ca,b,c表示对应的数的选中次数。
从最简单的ax+by=kax+by=kax+by=k入手分析:
by=k−axby=k-axby=k−ax by%x∈[0,x−1]by\%x \in{[0,x-1]}by%x∈[0,x−1] i⇒(i+y)%x,cost=y,i∈[0,x−1]i\Rightarrow(i+y)\%x,cost=y,i\in{[0,x-1]}i⇒(i+y)%x,cost=y,i∈[0,x−1]
也就是说由bybyby组成的值对xxx取余,可以将这些值根据余数分为xxx组,也就是同余类。并且从余数iii到余数(i+y)%x(i+y)\%x(i+y)%x的代价是yyy。
建图,边的信息为(i,(i+y)%x,y),i∈[0,x−1](i, (i+y)\%x, y), i\in{[0,x-1]}(i,(i+y)%x,y),i∈[0,x−1],表示从点iii到点(i+y)%x(i+y)\%x(i+y)%x的代价是yyy(当然也存在 by%x≠iby\%x\ne iby%x​=i这种情况)。
dis[i]dis[i]dis[i]表示满足by%x=iby\%x=iby%x=i的最小的bybyby,也就是最少要选取bbb次yyy,才能使by%x=i,b≥0by\%x=i,b≥0by%x=i,b≥0,也就是每个同余类中的最小值。
求dis[]dis[]dis[]就相当于在图中求最短路(我习惯用spfa),且dis[0]=0dis[0]=0dis[0]=0(因为0∗y%x=00*y\%x=00∗y%x=0)
求完dis[i]dis[i]dis[i]之后便得到了每个同余类中的最小值pip_ipi​,很明显pi+xp_i+xpi​+x和pip_ipi​属于同一个同余类中,并且pi+tx≠pj+tx,0≤i,j≤x−1,i≠jp_i+tx\ne p_j+tx,0≤i,j≤x-1,i\ne jpi​+tx​=pj​+tx,0≤i,j≤x−1,i​=j,这样就得得到了ax+by=kax+by=kax+by=k组成的所有的不同的kkk值。
对于ax+by+cz=kax+by+cz=kax+by+cz=k这种式子,按照同样的思路建图,边的信息为:
(i,(i+y)%x,y)(i, (i+y)\%x, y)(i,(i+y)%x,y)(i,(i+z)%x,z)(i, (i+z)\%x, z)(i,(i+z)%x,z)
对应的dis[i]dis[i]dis[i]表示满足by+cz%x=iby+cz\%x=iby+cz%x=i的最小的by+czby+czby+cz。
为了使图中的节点数最少,取这nnn个数中的最小值作为xxx,那么图中的边数为x(n−1)x(n-1)x(n−1)。

P3403

传送门

  • 题意:
    相当于求ax+by+cz+1ax+by+cz+1ax+by+cz+1能够组成多少个小于hhh的不同的数。
  • 思路:
    因为by+cz+1by+cz+1by+cz+1当b=0,c=0b=0,c=0b=0,c=0时得到111,所以根据dis[i]dis[i]dis[i]的定义有dis[1%x]=1dis[1\%x]=1dis[1%x]=1。
    求出dis[]dis[]dis[]后,可知ans=∑i=0x−1(h−dis[i]x+1)\displaystyle ans=\sum_{i=0}^{x-1} (\frac{h-dis[i]}{x}+1)ans=i=0∑x−1​(xh−dis[i]​+1),(h>=dis[i])(h>=dis[i])(h>=dis[i])
  • ac代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9+7;
const int maxn = 1e5+10;
struct node{int nxt, val;
};
vector<node> edge[maxn];
queue<int> q;
int x, y, z;
ll h;
ll dis[maxn];
bool inq[maxn];
void spfa()
{for(int i = 0; i <= x; i++) dis[i] = LLONG_MAX, inq[i] = false;dis[1%x] = 1;q.push(1%x); inq[1%x] = true;while(!q.empty()){int now = q.front(); q.pop(); inq[now] = false;for(auto e: edge[now]){int nxt = e.nxt, val = e.val;if(dis[now]+val<dis[nxt]){dis[nxt] = dis[now]+val;if(!inq[nxt]) q.push(nxt), inq[nxt] = true;}}}
}
int main()
{//freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);scanf("%lld %d %d %d", &h, &x, &y, &z);int p = min(x, min(y, z));if(y==p) swap(x, y);else if(z==p) swap(x, z);for(int i = 0; i < x; i++){edge[i].push_back({(i+y)%x, y});edge[i].push_back({(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\n", ans);return 0;
}

P2371

传送门

  • 题目:
    求∑aixi\sum{a_ix_i}∑ai​xi​的值有多少个在[l,r][l,r][l,r]内,xix_ixi​已知。
  • 思路:
    先求出∑aixi\sum{a_ix_i}∑ai​xi​有多少种小于等于h的值,记为cnt[h]cnt[h]cnt[h](这就是P3403要解决的问题)。
    ans=cnt[r]−cnt[l−1]ans=cnt[r]-cnt[l-1]ans=cnt[r]−cnt[l−1]
  • ac代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9+7;
const int maxn = 5e5+10;
struct node{int nxt, val;
};
vector<node> edge[maxn];
queue<int> q;
ll dis[maxn];
int a[20];
bool inq[maxn];
ll n, l, r;
void spfa()
{for(int i = 0; i <= a[1]; i++) dis[i] = LLONG_MAX;dis[0] = 0; q.push(0); inq[0] = true;while(!q.empty()){int now = q.front(); q.pop(); inq[now] = false;for(auto e: edge[now]){int nxt = e.nxt, val = e.val;if(dis[now]+val<dis[nxt]){dis[nxt] = dis[now]+val;if(!inq[nxt]) q.push(nxt), inq[nxt] = true;}}}
}
ll cnt(ll h)
{ll ans = 0;for(int i = 0; i < a[1]; i++)if(h>=dis[i]) ans += (h-dis[i])/a[1]+1;return ans;
}
int main()
{//freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);scanf("%lld %lld %lld", &n, &l, &r);int mi = INT_MAX;for(int i = 1; i <= n; i++) scanf("%d", &a[i]), mi = min(mi, a[i]);for(int i = 1; i <= n; i++) if(a[i]==mi) {swap(a[i], a[1]); break; }for(int i = 0; i < a[1]; i++)for(int j = 2; j <= n; j++)edge[i].push_back({(i+a[j])%a[1], a[j]});spfa();printf("%lld\n", cnt(r)-cnt(l-1));return 0;
}

P2662

传送门

  • 题目:
    题目转化为求∑aixi\sum{a_ix_i}∑ai​xi​不能组成的最大的数(根据题意可知指的是正数),xix_ixi​已知,不存在或最大值不能确定输出−1-1−1。
  • 思路:
    • 如果存在xi=1x_i=1xi​=1,那么可以得到任何正数,输出−1-1−1
    • 如果对于xxx的一个同余类iii,得不到相应的dis[i]dis[i]dis[i],说明对于满足w%x=iw\%x=iw%x=i的www,都不能由这些数得到,且www可以无限大,无法确定最大值,输出−1-1−1。
    • dis[i]dis[i]dis[i]是同余类iii里面的最小值,那么在同余类iii中,属于这个同余类但不可得到的最大数为dis[i]−xdis[i]-xdis[i]−x,那么ans=max{dis[i]−x},i∈[0,x−1]ans=max\{dis[i]-x\},i\in{[0,x-1]}ans=max{dis[i]−x},i∈[0,x−1]
  • ac代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 3e3+10;
struct node{int nxt, val;
};
vector<node> edge[maxn];
queue<int> q;
int n, m, x;
int a[110];
ll dis[maxn];
bool inq[maxn];
void spfa()
{for(int i = 0; i < x; i++) dis[i] = LLONG_MAX, inq[i] = false;dis[0] = 0; inq[0] = true;q.push(0);while(!q.empty()){int now = q.front(); q.pop(); inq[now] = false;for(auto e: edge[now]){int nxt = e.nxt, val = e.val;if(dis[now]+val<dis[nxt]){dis[nxt] = dis[now]+val;if(!inq[nxt]) q.push(nxt), inq[nxt] = true;}}}
}
int main()
{//freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);scanf("%d %d", &n, &m);for(int i = 1; i <= n; i++) scanf("%d", &a[i]);sort(a+1, a+1+n);if(m>=a[1]-1) {printf("-1\n"); return 0;}//任何长度都可以达到x = a[1]-m;for(int i = 1; i <= n; i++)for(int j = max(a[i-1]+1, a[i]-m); j <= a[i]; j++)//不统计重复的长度for(int k = 0; k < x; k++)edge[k].push_back({(k+j)%x, j});spfa();ll ans = 0;for(int i = 0; i < x; i++){if(dis[i] == LLONG_MAX) {printf("-1\n"); return 0;}//最大值不存在(可以无穷大)else ans = max(ans, dis[i]-x);//该同余类不能构成的最大的数}printf("%lld\n", ans);return 0;
}

牛客练习赛60:D

传送门

  • 题目:
    求满足ax+by+cz=kax+by+cz=kax+by+cz=k的一组解,a,b,ca,b,ca,b,c已知。
  • 思路:
    裸的同余最短路题,在求最短路的时候记录下bbb值在最短路的路径中出现的次数。
    当然本地也可以枚举z的值,用扩展欧几里得算法做,然后就是上模板了。
  • ac代码:
    • 同余最短路做法(正解):
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9+7;
const int maxn = 1e5+10;
int a, b, c;
ll k;
struct node{int nxt, val;
};
vector<node> edge[maxn];
queue<int> q;
ll dis[maxn], cntb[maxn];
bool inq[maxn];
void spfa()
{for(int i = 0; i < a; i++) dis[i] = LLONG_MAX, cntb[i] = 0;dis[0] = 0;q.push(0);while(!q.empty()){int now = q.front(); q.pop(); inq[now] = false;for(auto e: edge[now]){int nxt = e.nxt, val = e.val;if(dis[now]+val<dis[nxt]){dis[nxt] = dis[now]+val;cntb[nxt] = cntb[now];if(val==b) cntb[nxt]++;if(!inq[nxt]) inq[nxt] = true, q.push(nxt);}}}
}
int main()
{//freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);scanf("%d %d %d %lld", &a, &b, &c, &k);for(int i = 0; i < a; i++){edge[i].push_back({(i+b)%a, b});edge[i].push_back({(i+c)%a, c});}spfa();for(int i = 0; i < a; i++){if(k>=dis[i] && (k-dis[i])%a==0){printf("%lld %lld %lld\n", (k-dis[i])/a,  cntb[i], (dis[i]-cntb[i]*b)/c);break;}}return 0;
}
  • 扩展欧几里得算法:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9+7;
const int maxn = 1e3+10;
ll a, b, c, k;
ll exgcd(ll a, ll b, ll &x, ll &y)//ax+by,返回gcd(a, b)
{if(b == 0){x = 1;y = 0;return a;//gcd(a,b)}ll r = exgcd(b, a%b, x, y);//x1=y2 ,  y1=x2-a/b*y2ll t = x;x = y;y = t - a/b*y;return r;
}
bool linear_equation(ll a, ll b, ll c, ll &x, ll &y)//ax+by=c
{ll com = exgcd(a, b, x, y);if(c%com) return false;ll k = c/com;x *= k; y *= k;//一组解ll B = abs(b/com);x = ((x%B)+B)%B; //最小正整数解xy = (c-a*x)/b;if(x>=0 && y>=0) return true;else return false;
}
int main()
{scanf("%lld %lld %lld %lld", &a, &b, &c, &k);ll x, y;for(ll z = 0; z <= 100000; z++){if(linear_equation(a, b, k-z*c, x, y)){printf("%lld %lld %lld\n", x, y, z);break;}}return 0;
}

【同余最短路】P3403+P2371+P2662+牛客4853D相关推荐

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

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

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

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

  3. P2371 [国家集训队]墨墨的等式 ——同余最短路

    P2371 思路 同余最短路,自我感觉做这类题要注意两点. 1.虽然n的范围很小,但实际建图的点的数量为min{a_i} =5e5,边的数量为n∗ain * a_in∗ai​即12*5e5. 2.和上 ...

  4. 牛客 - 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 ...

  5. P2371 [国家集训队]墨墨的等式 同余最短路

    传送门 题意: 思路: 一个同于最短路的板子题,初始的时候值为0,所以设dis[0]=0dis[0]=0dis[0]=0,让后选择一个最小的a[i]a[i]a[i]作为basebasebase,跑一遍 ...

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

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

  7. 牛客网 最短路 Floyd算法 Dijkstra算法 Java大数

    链接:https://www.nowcoder.com/questionTerminal/a29d0b5eb46b4b90bfa22aa98cf5ff17 来源:牛客网 最短路径 热度指数:2992 ...

  8. 牛客网 New Game! 建图+最短路

    链接:https://www.nowcoder.com/acm/contest/201/L 来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 1048576K,其他语言20 ...

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

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

  10. HDU - 6071 Lazy Running 同余最短路 + 分层

    传送门 题意: 给定四个点构成一个环,给出四个点之间的距离,让后从222号点出发,最终回到222号点,求经过的距离>=k>=k>=k的最小距离. 思路: 由于从222开始,最终在22 ...

最新文章

  1. 实例解说.Net构架下的加密编程
  2. Objective-C面向对象之实现类
  3. visio取消首字母大写
  4. Python 深度学习目标检测评价指标 :mAP、Precision、Recall、AP、IOU等
  5. seaborn常用图
  6. 任务 进程 线程区别
  7. 打印系统所有维护了sales area的BP id
  8. MySQL 高级 —— MVCC 多版本并发控制
  9. 2019年你准备学C/C++,这些硬知识你应该知道!
  10. java 扫雷游戏_java的扫雷小游戏(超简单)
  11. 【Linux】Linux下 CURL如何发送http请求
  12. 《Pro Ogre 3D Programming》 读书笔记 之 第四章 开始使用OGRE (转)
  13. 评委打分安卓端与服务管理端的前期准备及操作关键点说明
  14. java:线程的六种状态
  15. ERP财务管理模块包括什么
  16. cp: omitting directory”错误的解释和解决办法
  17. signature=ac75cb7977a45c0f7d8a73dca59a4c27,合肥2016年8月24日至2016年9月5日交通违章查询...
  18. ColBERT(2020SIGIR)
  19. 基于Vue源码中e2e测试实践
  20. Java数组实现:一群人围成一圈从123报数 如果报到3就退出该圈中 直到最后一个人留下来!问其位置

热门文章

  1. CruiseControl初探
  2. Unity工程导入到AndroidStudio的一些注意事项
  3. 点击场景中的物件无法定位到Hierarchy
  4. c段服务器维护,服务器 多c段
  5. 断电MySQL起不来_急求mysql 断电后无法启动解决方法!!!!
  6. php修改另一个文件内容,php如何修改文件内容
  7. java if else重构_Java编程细节-重构-为什么 if-else 不是好代码
  8. 【转】JQuery中$.each 和$(selector).each()的区别详解
  9. java怎么用一行代码初始化ArrayList
  10. 解决windows 下 mysql命令行导入备份文件 查询时乱码的问题