1. 程式人生 > >【BZOJ2118】墨墨的等式 題解

【BZOJ2118】墨墨的等式 題解

2118: 墨墨的等式

Time Limit: 10 Sec Memory Limit: 259 MB
Description

墨墨突然對等式很感興趣,他正在研究a1x1+a2y2+…+anxn=B存在非負整數解的條件,他要求你編寫一個程式,給定N、{an}、以及B的取值範圍,求出有多少B可以使等式存在非負整數解。

Input

輸入的第一行包含3個正整數,分別表示N、BMin、BMax分別表示數列的長度、B的下界、B的上界。輸入的第二行包含N個整數,即數列{an}的值。

Output

輸出一個整數,表示有多少b可以使等式存在非負整數解。

Sample Input

2 5 10

3 5

Sample Output

5

HINT

對於100%的資料,N≤12,0≤ai≤5*10^5,1≤BMin≤BMax≤10^12。

Source
       這題總體來說並不難,很明顯,如果存在一個合法的解b,那麼b+ a[i]也一定是合法的,故我們只要對於 a[i]算出模其結果不同的合法的最小的數就能知道答案了。為了提高效率,我們取mn=min{a[i]}。
       一維陣列d[i]表示模mn等於i的最小的合法解,這可以用dijkstraspfa輕鬆求出;用calc(x)表示[0,x]中的合法解總數,則cal(x)=i=0mn1xd[i]mn+1(xd[i])
       最終答案即為:calc(bmax)calc(bmin1)

       注意忽略a[i]中的0 !!!

       附上程式碼:
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstdlib>
using namespace std;
int
n; long long bl,br; int a[13]; int mn=1e9; int cnt; long long d[500010]; bool inq[500010]; long long calc(long long x) { long long res=0; for(int i=0;i<mn;i++) { if(d[i]>x)continue; res+=(x-d[i])/mn+1; } return res; } int main() { scanf("%d%lld%lld",&n,&bl,&br); for(int i=1;i<=n;i++) { int x; scanf("%d",&x); if(!x)continue; a[++cnt]=x; mn=min(mn,a[i]); } for(int i=1;i<mn;i++)d[i]=1e18; queue<int>q; q.push(0); inq[0]=true; while(!q.empty()) { int x=q.front(); q.pop(); inq[x]=false; for(int i=1;i<=cnt;i++) { int y=(x+a[i])%mn; if(d[x]+a[i]<d[y]) { d[y]=d[x]+a[i]; if(!inq[y]) { inq[y]=true; q.push(y); } } } } printf("%lld",calc(br)-calc(bl-1)); return 0; }