同余最短路
同余最短路
同余最短路可以用于解决形如 "给定 \(n\) 个整数,求这 \(n\) 个整数能拼凑出多少的其他的整数( \(n\) 个整数可以重复选取)" 以及 "给定 \(n\) 个整数,求这 \(n\) 个整数不能拼凑出的最小(最大)的整数",或者 "至少要拼几次才能拼出模 \(k\) 余 \(p\) 的数 的问题的时候可以使用同余最短路的方法。
同余最短路利用同余来构造一些状态,可以达到优化空间复杂度的目的。
类比差分约束的方法,利用同余构造的这些状态可以看作单源最短路中的点,同余最短路的状态转移通常是这样的: \(f_{i+j}=f_i+j\) ,类似单源最短路中的 \(f_v=f_u+edge(u, v)\) 。
下面来看一下板题
Small Multiple
要求\(K\)的倍数?,不就是
考虑建立\(0\sim {K-1}\)z这些虚点存${\color{Red} 模K余i的数的最小数位和} $
如何转移,发现乘10后数位和不变,那么枚举乘10后加\(1\sim 9\)后这个数模\(K\)是多少不就行了
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n;
int vis[1001000],dp[1001000];
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >q;
void dij() {
memset(dp,0x3f,sizeof(dp));
for(int i=1;i<=9;i++) { q.push({i,i%n}),dp[i%n]=min(dp[i%n],i);}
// cout<<dp[0];
while(!q.empty()) {
int k=q.top().second;
q.pop();
if(vis[k]) continue;
vis[k]=1;
for(int i=0;i<=9;i++) {
int v=(k*10+i)%n;
if(dp[v]>i+dp[k]) {
dp[v]=i+dp[k];
q.push({dp[v],v});
}
}
}
}
————————————————
P3403
发现只能往上走,x,y,z,想想和刚才讲的有什么联系
设其中最小数为k,容易发现只要找到了第一个%k等于i的数v,那么v+k,v+2*k...是不是也都可以取到,那么答案呼之欲出了有没有
注意因为是余数所以每个点贡献是\(\frac{n-dis_i}{k}+1\) ,代码不放了和上面的差不多
————————————————
P2662
首先可以预处理出来哪些长度可以取到,取最小值s为模数,直接跑就行
稍微难一点是统计答案了,如果有不能取到的同余系,那么就无解,否则取
——————————————————
P2371
感觉思路挺一眼的,还是同余系吗,注意统计答案时如何查分就行了,贴一下我的
点击查看代码
for(int i=0;i<a[1];i++) {
ans+=max((r-dis[i]),0ll)/a[1]-max(l-1-dis[i],0ll)/a[1];
f(r>=dis[i]) ans++;
if(l-1>=dis[i]) ans--;
}

同余最短路的基础性内容
浙公网安备 33010602011771号