前置题目(双倍经验):[洛谷P3403]跳楼机
题目
求([l,r])中有多少个b可以使得方程(sum{a_ix_i = b})有非负整数解((nleq 12,l,rleq 10^{12}))
思路
问题等价于求(solve(r)-solve(l-1))
考虑怎么求(solve(r)),设(f_i)表示(x_1=0)时(只用其他的数)能得到的最小的模(a_1)为(i)的数
显然,(f_i)可以通过加(a_{2,3,...,n})变成转移给(f_{(i+a_{2,3,4....n})\%a_1}),将这个关系建边,跑一遍最短路即可得到(f)数组
由于(f)已经是不用(a_1)情况下可以得到的最小的值了,所以给(f)只加(a_1)就可以得到所有的合法情况;而根据(f)的定义,只在(f_i)上加(a_1)不会使得不同(i)出现(f_i+a_1x_1)相等的情况,所以分别处理每个(f_i)再加起来就行了
即(ans=sum{((r-f_i)/a_1 +1)})
P.S.注意特判存在(a_i=1)的情况;可以看出边数与(a_1)有关,取最小的(a)作为(a_1)(当然不取应该也没什么事)
Code
#include<bits/stdc++.h>
#define N 500005
#define Min(x,y) ((x)<(y)?(x):(y))
#define Max(x,y) ((x)>(y)?(x):(y))
using namespace std;
typedef long long ll;
int n,a[15];
ll l,r,dis[N];
bool ccf,vis[N];
struct Edge
{
int next,to,dis;
}edge[N*12];int head[N],cnt;
void add_edge(int from,int to,int dis)
{
edge[++cnt].next=head[from];
edge[cnt].to=to;
edge[cnt].dis=dis;
head[from]=cnt;
}
template <class T>
void read(T &x)
{
char c; int sign=1;
while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
while((c=getchar())>='0'&&c<='9') x=(x<<1)+(x<<3)+c-48; x*=sign;
}
void dijkstra()
{
priority_queue< pair<ll,int> > q;
memset(dis,100,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[0]=0;
q.push(make_pair(-dis[0],0));
while(!q.empty())
{
int u=q.top().second; q.pop();
if(vis[u]) continue;
vis[u]=1;
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
if(dis[v]>dis[u]+edge[i].dis)
{
dis[v]=dis[u]+edge[i].dis;
if(!vis[v]) q.push(make_pair(-dis[v],v));
}
}
}
}
int main()
{
read(n);read(l);read(r); --l;
for(int i=1;i<=n;++i) read(a[i]),ccf|=(a[i]==1);
if(ccf) { cout<<r-l<<endl; return 0; }
sort(a+1,a+n+1);
for(int i=0;i<a[1];++i)
for(int j=2;j<=n;++j)
add_edge(i,(i+a[j])%a[1],a[j]);
dijkstra();
ll ans=0;
for(int i=0;i<a[1];++i)
{
if(dis[i]<=r) ans+=(r-dis[i])/a[1]+1;
if(dis[i]<=l) ans-=(l-dis[i])/a[1]+1;
}
cout<<ans<<endl;
return 0;
}