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

题目

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

其实很简单的

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

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

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

我们发现我们能够凑出\(k\times mx+p\)就能凑出\((k+1)\times mx+p\),以及所有\(\%mx=p\)的数,前提是大于\(k\times mx+p\),所以对于每一个\(p\)找到最小的\(k\times 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\n",ans);
	return 0;
}
posted @ 2019-03-02 21:28  asuldb  阅读(127)  评论(0编辑  收藏  举报