[bzoj2118]墨墨的等式——同余最短路
题目大意:
墨墨突然对等式很感兴趣,他正在研究\(a_1x_1+a_2x_2+…+a_nx_n=B\)存在非负整数解的条件
他要求你编写一个程序,给定\(N,\{a_n\}\)以及\(B\)的取值范围,求出有多少B可以使等式存在非负整数解。
\(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]\times x+b\)。
建图其实也很简单。
每一个余数代表一个点,点u可以向点\((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;
}