墨墨的等式傳送門
這道題很神奇。
找餘數
如果 x 可以爲負數,那就變成了拓展歐幾里得,但 x 是非負整數,解決方法就不一樣了。我們找到所有 a 中最小的非負 a,記做 mn。如果能湊成 mn*x+k (k<a),那麼就能湊成 mn*(x+1)+k,也就是可以湊成 B~mn*x+k 之間所有與 mn*x+k 相差 mn 的數,所以我們就要找到對於每一個 k ( 1 <= k < mn ),最小的 mn*x+k 是多少。
dijkstra
記 dis[k] 爲最小的 mn*x+k,我們發現如果 mn* x+k_1+a_i = mn*y+k_2 ,那麼我們就可以用 dis[k_1] 更新 dis[k_2],即設 t = (k+a_i)%mn, f[t]=min( f[t], f[k]+a_i ),這很像求最短路,因此我們抽象地建一個圖,跑 dijkstra。這是在用形象的圖論解決抽象的數學問題,非常強。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long LL;
const int N=15, M=6000000, A=500010, INF1=1000000000;
const LL INF2=1000000000000000;
int n, mi, mn;
LL Bmn, Bmx;
LL dis[A];
int head[A], a[N];
bool vis[N];
struct Edge{
int to,next,w;
}e[M<<1];
inline void add(int u,int v,int w){
e[++mi] = (Edge){v,head[u],w};
head[u] = mi;
}
struct HeadNode{
LL w; int u;
bool operator < (const HeadNode & rhs) const{
return w>rhs.w;
}
};
priority_queue <HeadNode> Q;
inline void dijkstra(){
for(int i=1; i<mn; ++i) dis[i]=INF2;
Q.push((HeadNode){0,0}); dis[0]=0;
int u, v, p;
while(!Q.empty()){
HeadNode c = Q.top(); Q.pop();
u = c.u;
if(c.w!=dis[u]) continue;
for(p=head[u]; p; p=e[p].next){
v = e[p].to;
if(dis[v]>dis[u]+e[p].w){
dis[v] = dis[u]+e[p].w;
Q.push((HeadNode){dis[v],v});
}
}
}
}
inline LL query(LL x){
LL ans=0;
for(int i=0; i<mn; ++i) if(dis[i]<=x){
ans += (x-dis[i])/mn+1;
}
return ans;
}
int main(){
mn = INF1;
scanf("%d%lld%lld",&n,&Bmn,&Bmx);
for(int i=1; i<=n; ++i){
scanf("%d",&a[i]);
if(!a[i]){ n--; i--; continue; }
mn = min(mn,a[i]); // 找最小的 a
}
for(int i=1; i<=n; ++i) if(a[i]%mn) vis[i]=true;
for(int i=0, j; i<mn; ++i){
for(j=1; j<=n; ++j) if(vis[j]){
add(i,(i+a[j])%mn,a[j]); // 建圖
}
}
dijkstra();
printf("%lld\n",query(Bmx)-query(Bmn-1));
while(1);
return 0;
}