[国家集训队]墨墨的等式

题目

曾经我觉得这道题好难啊,同余类最短路好难啊

其实很简单的

我们找到所有系数中最小的那一个(mx)

让所有运算都在(mod mx)的意义下进行

我们能得到的数就是(k imes mx+p),当然反映在运算结果上我们就只能看到(p)

我们发现我们能够凑出(k imes mx+p)就能凑出((k+1) imes mx+p),以及所有(\%mx=p)的数,前提是大于(k imes mx+p),所以对于每一个(p)找到最小的(k imes mx+p)就好了

这里最短路实现就好了,对于(i)节点我们向((i+a_j)\%mx)连一条代价为(a_j)的边,跑最短路就是了

由于([l,r])里的数也可以被放到(mod mx)的独立剩余系里,于是我们看一下每一个(p)能凑成那些数,这样统计就好了

代码

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define LL long long
#define int long long
#define re register
#define maxn 15
inline LL read() {
	char c=getchar();LL x=0;while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
std::queue<int> q;
LL d[500005];int vis[500005];
int n,a[maxn],mx=999999999;
LL l,r;
signed main() {
	n=read(),l=read(),r=read();
	for(re int i=1;i<=n;i++) a[i]=read(),mx=min(mx,a[i]);
	memset(d,20,sizeof(d));d[0]=0;q.push(0);
	while(!q.empty()) {
		int k=q.front();q.pop();vis[k]=0;
		for(re int i=1;i<=n;i++)
		if(d[(k+a[i])%mx]>d[k]+a[i]) {
			d[(k+a[i])%mx]=d[k]+a[i];
			if(!vis[(k+a[i])%mx]) 
				vis[(k+a[i])%mx]=1,q.push((k+a[i])%mx);
		}
	}
	LL ans=0;
	for(re int i=0;i<mx;i++) {
		if(d[i]>r) continue;
		if(d[i]<=l) {
			LL t1=l/mx,t2=r/mx;
			ans+=t2-t1+1;
			if(r%mx<i) ans--;if(l%mx>i) ans--;
		}
		else ans+=(r-d[i])/mx,ans++;
	}
	printf("%lld
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/asuldb/p/10463043.html