bzoj 2118: 墨墨的等式 spfa

题目:

墨墨突然对等式很感兴趣,他正在研究(a_1x_1+a_2y_2+ ... +a_nx_n=B)存在非负整数解的条件,他要求你编写一个程序,给定(N,{a_n})以及(B)的取值范围,求出有多少(B)可以使等式存在非负整数解。

题解:

首先我们发现 : 如果我们能够通过选取一些数凑到(x),那么我们肯定能够凑到$x + a_1 ,x + 2a_1 ,x + 3a_1, ... ( 所以我们考虑在)mod a_1(的剩余系下进行操作. 记)f[x](表示取到可以用)k*a_1 + x(表示的数的最小的)k$
这个dp我们可以直接利用最短路算法求解.

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(ll &x){
    x=0;char ch;bool flag = false;
    while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
    while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
const ll maxn = 500500;
const ll lim = maxn<<1;
ll a[maxn],dis[maxn],q[lim + 10],l,r,n;
bool inq[maxn];
void spfa(){
    memset(dis,0x3f,sizeof dis);
    l = 0;r = -1;
    dis[0] = 0;q[++r] = 0;
    inq[0] = true;
    while(l <= r){
        ll u = q[l++];
        for(ll i=2;i<=n;++i){
            ll v = (u + a[i]) % a[1];
            if( dis[v] > dis[u] + (u+a[i])/a[1]){
                dis[v] = dis[u] + (u+a[i])/a[1];
                if(!inq[v]){
                    q[++r] = v;
                    inq[v] = true;
                }
            }
        }inq[u] = false;
    }
}
inline ll calc(ll x){
    ll ret = 0;
    for(ll i=0;i<a[1];++i){
        ret += max((x/a[1] + ((x % a[1]) >= i)) - dis[i],0LL);
    }return ret;
}
int main(){
    ll L,R;read(n);read(L);read(R);
    ll pos = 0;
    for(ll i=1;i<=n;++i){
        read(a[i]);
        if(pos == 0 || a[pos] > a[i]) pos = i;
    }swap(a[pos],a[1]);
    if(a[1] == 0) return puts("0");
    spfa();
    printf("%lld
",calc(R) - calc(L-1));
    getchar();getchar();
    return 0;
}
原文地址:https://www.cnblogs.com/Skyminer/p/6584462.html