1. 程式人生 > >[bzoj2118]墨墨的等式——同餘最短路 dalaos' blogs Some Links

[bzoj2118]墨墨的等式——同餘最短路 dalaos' blogs Some Links

題目大意:

墨墨突然對等式很感興趣,他正在研究 a 1 x 1 + a 2

x 2 + + a n x
n
= B a_1x_1+a_2x_2+…+a_nx_n=B 存在非負整數解的條件
他要求你編寫一個程式,給定 N , {
a n } N,\{a_n\}
以及 B B 的取值範圍,求出有多少B可以使等式存在非負整數解。
N 12 , 0 a i 5 × 1 0 5 , 1 B m i n B m a x 1 0 12 N\leq12,0\leq a_i\leq 5\times 10^5,1\leq B_{min} \leq B_{max} \leq 10^{12}

思路:

題目中要求的是非負整數解,於是我們可以把每一個數看成一個物品,求所有物品可以組成的體積。
但是直接跑揹包顯然是接受不了的
考慮到最後的體積集合,我們把它按照a[1](也就是任意一個數)的剩餘類分類。
雖然可以組成的體積種類很多,但是按照剩餘類分類之後體積的種類就只有5e5種 於是我們只需要求出每一個剩餘類最小的體積就好了,每一個合法的體積一定可以表示成 a [ 1 ] × x + b a[1]\times x+b
建圖其實也很簡單。
每一個餘數代表一個點,點u可以向點 ( v + a [ j ] ) m o d    a [ 1 ] (v+a[j])\mod a[1] 連一條權值為a[j]的邊。
從0開始單源最短路即可以得到所有餘數的最小體積。

#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define debug(x) cout<<#x<<"="<<x<<endl
#define pii pair<ll,int>
#define fi first
#define se second
#define mk make_pair
typedef long long ll;

using namespace std;

void File(){
    freopen("bzoj2118.in","r",stdin);
    freopen("bzoj2118.out","w",stdout);
}

template<typename T>void read(T &_){
    T __=0,mul=1; char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')mul=-1;
        ch=getchar();
    }
    while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
    _=__*mul;
}

const int maxn=20;
const int maxm=5e5+10;
const int maxe=6e6+10;
int n,a[maxn];
ll b0,b1,ans,w[maxe],dis[maxm];
int beg[maxm],to[maxe],las[maxe],cnte=1;

void add(int u,int v,ll val){las[++cnte]=beg[u]; beg[u]=cnte; to[cnte]=v; w[cnte]=val;}

void init(){
    read(n); read(b0); read(b1);
    REP(i,1,n)read(a[i]);
    REP(i,0,a[1]-1)REP(j,1,n)
        add(i,(i+a[j])%a[1],a[j]);
}

priority_queue< pii,vector<pii>,greater<pii> >qu;
void Dijkstra(){
    memset(dis,63,sizeof(dis));
    dis[0]=0; qu.push(mk(0,0));
    while(!qu.empty()){
        ll d=qu.top().fi; int u=qu.top().se;
        qu.pop();
        if(dis[u]!=d)continue;
        for(int i=beg[u];i;i=las[i]){
            int v=to[i];
            if(d+w[i]<dis[v]){
                dis[v]=d+w[i];
                qu.push(mk(dis[v],v));
            }
        }
    }
}

void work(){
    REP(i,0,a[1]-1){
        ll val=dis[i];
        if(val<b0)ans+=(b1-val)/a[1]-(b0-val-1)/a[1];
        else if(val<=b1)ans+=(b1-val)/a[1]+1;
    }
    printf("%lld\n",ans);
}

int main(){
//	File();
    init();
    Dijkstra();
    work();
    return 0;
}