给定
n
,
a
1
…
n
,
l
,
r
n, a_{1\dots n}, l, r
n,a1…n,l,r,求出有多少
b
∈
[
l
,
r
]
b\in[l,r]
b∈[l,r] 可以使
∑
i
=
1
n
a
i
x
i
=
b
\sum_{i=1}^n a_ix_i=b
∑i=1naixi=b 存在非负整数解。
n
≤
12
n \le 12
n≤12,
0
≤
a
i
≤
5
×
1
0
5
0 \le a_i \le 5\times 10^5
0≤ai≤5×105,
1
≤
l
≤
r
≤
1
0
12
1 \le l \le r \le 10^{12}
1≤l≤r≤1012。
算是个思维题了(基本上没人会想到这是道最短路题)。首先建图,定义
m
i
n
n
minn
minn 为所有
a
i
a_i
ai 中最小的一个,同时将其他
a
a
a 中元素前移一位。这里定义
d
i
s
i
dis_i
disi 是模
m
i
n
n
minn
minn 意义下的,同时
d
i
s
i
dis_i
disi 表示模
m
i
n
n
minn
minn 的所有符合条件的
b
b
b 中最小的一个。容易得知在
d
i
s
i
dis_i
disi 之后的所有模
m
i
n
n
minn
minn 余
i
i
i 的数都符合条件(直接加上若干个
m
i
n
n
minn
minn )。那么我们可以得到在
[
0
,
r
]
[0,r]
[0,r] 中答案的式子:
∑
i
=
0
m
i
n
n
−
1
r
−
d
i
s
i
m
i
n
n
+
1
(
i
f
d
i
s
i
≤
r
)
\sum_{i=0}^{minn-1} \frac{r-dis_i}{minn} +1(if\,\,\,dis_i\leq r)
i=0∑minn−1minnr−disi+1(ifdisi≤r)
解释一下,
r
−
d
i
s
i
r-dis_i
r−disi 表示剩下的数,除以循环节
m
i
n
n
minn
minn 得到之后还有多少符合条件的数(向下取整),再加上自己(
+
1
+1
+1 )就是答案。
再说如何建图,对于其他 a a a 中元素而言,假设现在是第 j j j 个数,它可以在 a i a_i ai 个数之后到达 ( j + a i ) m o d m i n n (j+a_i)\,\,mod\,\,minn (j+ai)modminn ,所以可以建条边 j − > ( j + a i ) m o d m i n n j->(j+a_i)\,\,mod\,\,minn j−>(j+ai)modminn ,长度为 a i a_i ai 。把所有边建起来就好了,然后再跑一遍最短路,起点是0,初始 d i s 0 = 0 dis_0=0 dis0=0 ,其他Inf。
处理 [ l , r ] [l,r] [l,r] 区间转化成处理 [ 0 , l − 1 ] [0,l-1] [0,l−1] 和 [ 0 , r ] [0,r] [0,r] 两段,再相减即是答案。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
struct edge{
int to,w,nxt;
}e[6000005];
ll a[25],n,l,r,minn,head[500005],ecnt;
ll dis[500005],bj[500005];
queue <int> Q;
void adde(int u,int v,int w){
e[++ecnt].nxt=head[u];
head[u]=ecnt;
e[ecnt].to=v;
e[ecnt].w=w;
}
void SPFA(){
dis[0]=0;
Q.push(0);
while(!Q.empty()){
int u=Q.front();
Q.pop();
bj[u]=0;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(dis[v]>dis[u]+e[i].w){
dis[v]=dis[u]+e[i].w;
if(bj[v]==0){
bj[v]=1;
Q.push(v);
}
}
}
}
}
ll ans(ll num){
ll ret=0;
for(int i=0;i<minn;++i){
if(num>=dis[i])
ret+=(num-dis[i])/minn+1;
}
return ret;
}
int main(){
scanf("%lld %lld %lld",&n,&l,&r);
scanf("%lld",&minn);
for(int i=1;i<=n-1;++i){
scanf("%lld",&a[i]);
if(a[i]<minn){
swap(a[i],minn);
}
}
memset(dis,0x3f,sizeof(dis));
for(int i=0;i<=minn-1;++i){
for(int j=1;j<=n-1;++j){
adde(i,(i+a[j])%minn,a[j]);
}
}
SPFA();
printf("%lld",ans(r)-ans(l-1));
return 0;
}