当前位置: 首页 > news >正文

同余最短路学习笔记

同余最短路是用来解决:形如「给定 \(n\) 个整数,求这 \(n\) 个整数能拼凑出多少的其他整数(\(n\) 个整数可以重复取)」,以及「给定 \(n\) 个整数,求这 \(n\) 个整数不能拼凑出的最小(最大)的整数」,或者「至少要拼几次才能拼出模 \(k\)\(p\) 的数」的问题时使用的算法。
核心思想是把无限多个状态压缩到有限个余数里。
先来看模板题:P3403 跳楼机
询问用 \(x,y,z\) 三个数能表示出多少个 \([0,h-1]\) 内的数。\(x,y,z \le 10^5\)\(h \le 2^{63}-1\)
显然把所有数的状态都搞出来是不可能的,先考虑只用 \(x\) 这一个数表示。那所有数就都写成了 \(ax+k\) 的形式,那只要我们让 \(by+cz \equiv k (\bmod x)\),就可以表示余数为 \(k\) 的数了,那么就建立图论模型,\(i\) 号点表示余数为 \(i\) 的数,\(dis_i\) 表示最小的能用 \(y,z\) 表示的同余 \(i\) 的数,我们让 \(i\)\((i+y) \bmod x\) 连边,边权为 \(y\)\(i\)\((i+z) \bmod x\) 连边,边权为 \(z\),跑最短路就得到每个 \(dis_i\)
那么 \(\frac{h-dis_i}{x}+1\),就是每个同余系的大小了。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define _int __int128
#define ull unsigned long long
#define pii pair<int,int>
#define fst first
#define scd second
#define pq priority_queue
#define mkp make_pair
#define popcount(x) __builtin_popcount(x)
#define endl '\n'
int h,x,y,z;
const int N = 1e5+10;
struct edge{int v,w;
};
vector<edge>g[N];
int dis[N],vis[N];
void dijkstra(){memset(dis,0x3f,sizeof(dis));pq<pii,vector<pii>,greater<pii> >q;dis[1%x]=1;q.push({1,1%x});while(!q.empty()){int u=q.top().scd,d=q.top().fst;q.pop();if(vis[u])continue;vis[u]=1;for(auto [v,w]:g[u]){if(dis[v]>dis[u]+w){dis[v]=dis[u]+w;q.push({dis[v],v});}}}
}
signed main(){ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);cin>>h>>x>>y>>z;if(x==1||y==1||z==1){cout<<h<<endl;return 0;}for(int i=0;i<x;i++){g[i].push_back({(i+y)%x,y});g[i].push_back({(i+z)%x,z});}dijkstra();int ans=0;for(int i=0;i<x;i++){if(h>=dis[i])ans+=(h-dis[i])/x+1;}cout<<ans;return 0;
}

例题:ARC084B
\(1\) 开始,我们可以通过加一来让数位之和加一,乘十则数位之和不变来表示出所有数。
那么每个余数代表一整个同余系的最小值,只要 \(i\)\((i+1) \bmod k\) 连权值为 \(1\) 的边,\(i\)\((i \times 10) \bmod k\) 连权值为 \(0\) 的边,然后就随便处理了。答案为 \(dis_0\)

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define _int __int128
#define ull unsigned long long
#define pii pair<int,int>
#define fst first
#define scd second
#define pq priority_queue
#define mkp make_pair
#define popcount(x) __builtin_popcount(x)
#define endl '\n'
int k;
const int N = 1e5+10;
struct edge{int v,w;
};
vector<edge>g[N];
int dis[N],vis[N];
void dijkstra(){memset(dis,0x3f,sizeof(dis));pq<pii,vector<pii>,greater<pii> >q;dis[1]=1;q.push({1,1});while(!q.empty()){int u=q.top().scd,d=q.top().fst;q.pop();if(vis[u])continue;vis[u]=1;for(auto [v,w]:g[u]){if(dis[v]>dis[u]+w){dis[v]=dis[u]+w;q.push({dis[v],v});}}}
}
signed main(){ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);cin>>k;for(int i=0;i<k;i++){g[i].push_back({(i+1)%k,1});g[i].push_back({(i*10)%k,0});}dijkstra();cout<<dis[0]<<endl;return 0;
}

转圈技巧
观察这个同余最短路的图,我们会发现存在环。我们把这些数看作一个个加入的,假设模数是 \(m\),当前加入了一个数是 \(x\),则每一个点 \(i\)\((i+x)\bmod m\) 连边,会形成 \(\gcd(x,m)\) 个环。
证明:形成环就是要从当前节点走若干步之后回到远处,则 \(i+k \times x \equiv i(\bmod m)\)。那 \(k \times x\) 就是 \(m\) 的倍数,而我们要找最小的 \(kx\),就是 \(lcm(m,x)\)。又因为 \(lcm(m,x)=\frac{m\times x}{\gcd(m,x)}\),所以环的大小为 \(\frac{m}{\gcd(m,x)}\),那环的个数就是总点数除以每个环的点数,即 \(\gcd(x,m)\)
那我们就可以在每个环上更新最短路,可以先绕环一圈找到 \(dis\) 最小的节点,从这个节点出发。也可以直接转两圈,保证每个节点都更新到了。
绕一次环是 \(O(m)\) 的,所以总时间复杂度 \(O(nm)\),优于 dijkstra 的 \(O(nm \log m)\)

    m=a[1];for(int i=2;i<=n;i++){int x=a[i];int num=__gcd(x,m);for(int j=0;j<num;j++){int p=j;for(int k=0;k<2*m/num;k++){int nx=(p+x)%m;dis[nx]=min(dis[nx],dis[p]+x);p=nx;}}}

P9140 THUPC2023初赛 背包
这个题在可行性的基础上还加入了权值,选择一个数 \(v_i\) 能获得 \(c_i\) 的利益,那么 \(q\) 次询问恰好拼出 \(V\) 的最大利益是多少,不能拼出输出 \(-1\)\(v_i \le 10^5\)\(10^{11} \le V \le 10^{12}\)
因为 \(v\) 很大,我们可以尽可能多的选择性价比最大的物品,即 \(\frac{c_i}{v_i}\) 最大的物品,记其 \(v_i\)\(m\)\(c_i\)\(w\)。因为只选这种物品可能不能刚好拼出 \(V\),所以我们要考虑用其他的物品替换掉一部分 \(i\)。设 \(V \bmod m = r\),那么我们就要去拼出 \(r\) 这个余数,显然有 \(m\) 种。
我们就设 \(f_r\) 表示当余数为 \(r\) 时,其他物品的贡献减去替换掉的 \(i\) 的贡献的最大值。
要令 \(W_总=w \times \lfloor \frac{V-V_{other}}{m} \rfloor + W_{other}\)。因为 \(w \times \lfloor \frac{V}{m} \rfloor\) 是固定的,那么只要考虑后面的 \(W_{other} - w \times \lfloor \frac{V_{other}}{m} \rfloor\),这就是 \(f_r\) 要维护的式子。
如果新加入了物品 \((v_i,c_i)\),令 \(d_i=\frac{v_i}{m}\),那么对它转圈,假设从余数 \(p\) 转移到 \(q\),则 \(q = (p+v_i) \bmod m\),如果 \(q \ge m\),说明我们又替换掉了一整个最佳物品,那 \(d=d_i+1\),所以转移式为 \(f_q=f_p+c_i-d*w\)。记得 \(f_i (i>0)\) 初值都设为 \(-10^{18}\),如果最后依然等于此值说明拼不出 \(r\) 这个余数。
对于每个询问,\(r=V \bmod m\)\(ans = f_r+\frac{V}{m}\times w\)

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define _int __int128
#define ull unsigned long long
#define pii pair<int,int>
#define fst first
#define scd second
#define pq priority_queue
#define mkp make_pair
#define popcount(x) __builtin_popcount(x)
#define endl '\n'
int n,m,q,w;
const int N = 1e5+10,inf=1e18;
int v[55],c[55],d[55],to[55];
int f[N];
signed main(){ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);cin>>n>>q;m=1;for(int i=1;i<=n;i++){cin>>v[i]>>c[i];if(w*v[i]<m*c[i]){w=c[i],m=v[i];}}for(int i=1;i<=n;i++){d[i]=v[i]/m;to[i]=v[i]%m;}for(int i=1;i<m;i++)f[i]=-inf;for(int i=1;i<=n;i++){int num=__gcd(v[i],m);for(int j=0;j<num;j++){int p=j;for(int k=0;k<2*m/num;k++){int q=p+to[i],x=d[i];if(q>=m)q-=m,x++;f[q]=max(f[q],f[p]+c[i]-x*w);p=q;}}}while(q--){int V,ans=0;cin>>V;int p=V%m;ans=f[p]+V/m*w;if(ans<-1e17)cout<<-1<<endl;else cout<<ans<<endl;}return 0;
}
http://www.jsqmd.com/news/1387652/

相关文章:

  • Candle:为什么这款开源GRBL控制器能让你的CNC加工事半功倍?
  • TransUNet完整训练教程:从零开始掌握医学图像分割的终极指南
  • 基于MCP协议构建天气查询工具:从原理到Node.js实战
  • Automa终极指南:5种循环自动化技巧解决重复性任务难题
  • 智能渗透测试代理:基于大语言模型的自动化安全评估解决方案
  • 电商大厂Java面试:从Spring Boot、JPA、微服务到Redis、Kafka、Spring Security与监控,谢飞机的爆笑三轮问答
  • 2026年哈尔滨双城区保暖服饰源头工厂靠谱推荐:马员外服饰全产业链实力解析 - 小随科技
  • 5.5m/s自主穿行密林!FlowPilot:用双流Transformer把世界模型塞进了无人机
  • Software House GSTAR004 控制器模块
  • 2026年西安长安区保暖服饰源头工厂靠谱推荐:马员外服饰全产业链实力解析 - 科技快讯
  • TCP与UDP的核心差异与性能优化实战
  • 漳浦县建设局网站:深度解读本地城建脉搏与民生服务新范式
  • 嵌入式软件岗和硬件岗怎么选?
  • 揭秘深圳自适应网站建设价格:2024年真实成本大起底与避坑指南
  • 华为MetaERP 本质上是两套ERP对“调整期间到底是什么“这一会计概念的建模差异——Oracle把它当成GL层的“年结调整容器“,SAP则把它设计成“受控的审计调整虚拟期间“。这两种哲学直接影响资
  • 从E-R图到SQL实战:学生选课系统数据库设计与优化全解析
  • 深入解读柳州市城乡建设局网站功能与服务指南如何高效利用官方平台
  • 从混乱到秩序:5人团队90天1200次实验,复现率从62%飙升至97%的实战指南
  • HttpAsyncClient长连接Connection reset问题排查与优化
  • 深入理解electron-vibrancy源码:跨平台实现原理剖析
  • 2026年哈尔滨双城区保暖服饰源头工厂靠谱推荐:马员外服饰全产业链实力解析 - 科技快讯
  • 2026年西安长安区保暖服饰源头工厂靠谱推荐:马员外服饰全产业链实力解析 - 子柔传媒
  • 别怕AI抢饭碗!测试新人用AI的第一天,只花了20分钟就写出了人生第一条自动化脚本
  • 创作者如何建立个人复盘系统:从数据驱动到持续成长
  • Threebox性能测试报告:万级3D对象渲染优化方案
  • LocationSimulator 终极指南:5分钟掌握iOS设备位置模拟技巧
  • 企业建站必问:网站建设空间一般多大?揭秘服务器配置背后的真相与陷阱
  • 2026年石家庄新乐市保暖服饰源头工厂靠谱推荐:马员外服饰全产业链实力解析 - 企业新闻快传
  • PhpStorm安装配置全指南:PHP开发必备IDE
  • 苏州地址挂靠每年都需要续费吗?不续费如何应对?