Loading

同余最短路

同余最短路

同余最短路可以用于解决形如 "给定 \(n\) 个整数,求这 \(n\) 个整数能拼凑出多少的其他的整数( \(n\) 个整数可以重复选取)" 以及 "给定 \(n\) 个整数,求这 \(n\) 个整数不能拼凑出的最小(最大)的整数",或者 "至少要拼几次才能拼出模 \(k\)\(p\) 的数 的问题的时候可以使用同余最短路的方法。

同余最短路利用同余来构造一些状态,可以达到优化空间复杂度的目的。
类比差分约束的方法,利用同余构造的这些状态可以看作单源最短路中的点,同余最短路的状态转移通常是这样的: \(f_{i+j}=f_i+j\) ,类似单源最短路中的 \(f_v=f_u+edge(u, v)\)

下面来看一下板题
Small Multiple

要求\(K\)的倍数?,不就是K等于0

考虑建立\(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为模数,直接跑就行

稍微难一点是统计答案了,如果有不能取到的同余系,那么就无解,否则取maxi=0s1disis
——————————————————
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--;
}
posted @ 2024-12-13 20:04  Mortis_Life  阅读(78)  评论(0)    收藏  举报