Loading

同余最短路

同余最短路

题型

  • 给定 \(n\) 个整数,求这 \(n\) 个整数能拼凑出多少的其他整数( \(n\) 个整数可以重复取)
  • 给定 \(n\) 个整数,求这 \(n\) 个整数不能拼凑出的最小(最大)的整数
  • 至少要拼几次才能拼出模 \(k\)\(p\) 的数

例题

P3403 跳楼机

题意

给定 \(x,y,z\) ,问对于 \(k\in [1,n]\) 有多少个 \(k\) 能满足 \(ax+by+cz=k\) \((a,b,c\in N^+)\)

思路

\(d(i)\) 表示仅通过操作 \(2,3\) \((a=0)\) 能达到的\(\mod x=i\) 的最小楼层

那么可得

\(d(i+y)=d(i)+y\) \(d(i+z)=d(i)+z\)

最短路的求法是:\(dis(x)=dis(y)+edge(x,y)\)

类比一下,让 \(i+y\)\(i+z\) 变成点,\(y,z\) 变成边权,通过跑最短路求得 \(d(1\sim x-1)\)

因为 \(1\) 操作可以每次让楼层加 \(x\) ,所以最后的答案就是

\[\sum_{i=1}^{x-1} \frac{h-d(i)}{x} +1 \]

\(+1\) 因为算当前楼

注意尽量选较小的 \(x\) 当作模数,可以省时空复杂度

#include<bits/stdc++.h>
using namespace std;

#define pb push_back
#define mp make_pair
#define pii pair <int,int>
#define int long long

const int N=1e5+5;
const int inf=2e18;

inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}

int h,x,y,z,ans;
int d[N];
bool vis[N];
vector <pii> G[N];

inline void add(int a,int b,int c){
	G[a].pb(mp(b,c));
}

inline void SPFA(){
	queue <int> q;
	for(int i=0;i<N;++i) d[i]=inf;
	d[1]=1,vis[1]=1;
	q.push(1);
	while(!q.empty()){
		int u=q.front();q.pop();
		vis[u]=0;
		for(auto y:G[u]){
			int v=y.first,val=y.second;
			if(d[v]>d[u]+val){
				d[v]=d[u]+val;
				if(!vis[v]){
					q.push(v);
					vis[v]=1;
				}
			}
		}
	}
}

signed main(){
	h=read(),x=read(),y=read(),z=read();
	if(x>y) swap(x,y);
	if(x>z) swap(x,z);
	if(x==1||y==1||z==1){
		cout<<h<<endl;
		return 0;
	}
	for(int i=0;i<x;++i){
		add(i,(i+z)%x,z);
		add(i,(i+y)%x,y);
	}
	SPFA();
	for(int i=0;i<x;++i) if(h>=d[i]) ans+=(h-d[i])/x+1;
	cout<<ans;
}

P2371 墨墨的等式

题意

给定 \(a[1\sim n]\) ,问对于 \(k\in [l,r]\) 有多少个 \(k\) 能满足 \(\sum_{i=1}^n a[i]\cdot x[i]\) \((x[1\sim n]\in N^+)\)

思路

类似于跳楼机的做法

但是要算出 \(1\sim r\)\(1\sim l-1\) 的答案,然后两个相减即为所求

#include<bits/stdc++.h>
using namespace std;

#define pb push_back
#define mp make_pair
#define pii pair <int,int>
#define int long long

const int N=5e5+5;
const int inf=2e18;

inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}

int n,m,l,r,mn=inf;
int d[N],a[N];
bool vis[N];
vector <pii> G[N];

inline void add(int a,int b,int c){
	G[a].pb(mp(b,c));
}

inline void SPFA(){
	queue <int> q;
	for(int i=0;i<N;++i) d[i]=inf;
	d[0]=0,vis[0]=1;
	q.push(0);
	while(!q.empty()){
		int u=q.front();q.pop();
		vis[u]=0;
		for(auto y:G[u]){
			int v=y.first,val=y.second;
			if(d[v]>d[u]+val){
				d[v]=d[u]+val;
				if(!vis[v]){
					q.push(v);
					vis[v]=1;
				}
			}
		}
	}
}

inline int query(int h){
	int ans=0;
	for(int i=0;i<mn;++i) if(h>=d[i]) ans+=(h-d[i])/mn+1;
	return ans;
}

signed main(){
	n=read(),l=read(),r=read();
	for(int i=1;i<=n;++i){
		int x=read();
		if(x){
			a[++m]=x;
			mn=min(mn,x);
		}
	}
	for(int i=0;i<mn;++i)
		for(int j=1;j<=m;++j)
			if(a[j]!=mn)
				add(i,(i+a[j])%mn,a[j]);
	SPFA();
	cout<<query(r)-query(l-1)<<endl;
}
posted @ 2022-07-06 18:10  Into_qwq  阅读(13)  评论(0编辑  收藏  举报