[国家集训队]墨墨的等式
曾经我觉得这道题好难啊,同余类最短路好难啊
其实很简单的
我们找到所有系数中最小的那一个\(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;
}