不愧是国家集训队的题目,神仙题啊
题目所让求的实际上是
(
∑
i
=
1
n
a
i
x
i
)
(\sum_{i=1}^n a_ix_i)
(∑i=1naixi)在所给定的
[
l
,
r
]
[l,r]
[l,r]区间内的取值个数
我们令
(
∑
i
=
1
n
a
i
x
i
)
(\sum_{i=1}^n a_ix_i)
(∑i=1naixi)为
S
S
S
我们考虑对于
S
S
S膜
a
i
a_i
ai的值,假设
S
=
k
∗
a
i
+
r
e
s
t
S=k*a_i+rest
S=k∗ai+rest,如果存在方式可以表示出S的话,那么一定存在
S
′
=
(
k
+
1
)
∗
a
i
+
r
e
s
t
S^{'}=(k+1)*a_i+rest
S′=(k+1)∗ai+rest,所以在这种情况下,S提供的贡献就是下式
A
N
S
+
=
{
B
m
a
x
−
S
)
/
a
i
+
1
;
S
∈
[
B
m
i
n
,
B
m
a
x
]
(
B
m
a
x
−
S
)
/
a
i
−
(
B
m
i
n
−
S
)
/
a
i
S
∈
[
0
,
B
m
i
n
)
ANS += \begin{cases} B_{max}-S)/a_i+1; & \text{S $\in[B_{min},B_{max}]$ } \\ (B_{max}-S)/a_i-(B_{min}-S)/a_i & \text{S $ \in[0,B_{min}) $} \end{cases}
ANS+={Bmax−S)/ai+1;(Bmax−S)/ai−(Bmin−S)/aiS ∈[Bmin,Bmax] S ∈[0,Bmin)
那么接下来我们需要考虑两件事
如何获取S
如何确定 a i a_i ai的值
通过上式不难看出
a
i
a_i
ai是越小越好的,所以取
a
a
a数组中最小值就好
那么S呢,因为对
a
i
a_i
ai取了模,所以对我们更重要的实际上是能达到
r
e
s
t
rest
rest的最小值是多少,因为取模后总共只有
a
i
−
1
a_i-1
ai−1种取值,比如膜
a
i
a_i
ai后是
r
e
s
t
rest
rest,那么
k
1
∗
a
i
+
r
e
s
t
k_1*a_i+rest
k1∗ai+rest和
k
2
∗
a
i
+
r
e
s
t
k_2*a_i+rest
k2∗ai+rest在
k
1
<
k
2
k_1<k_2
k1<k2的情况下前者一定优于后者,前者对于答案的贡献一定包含后者,所以后者是无用的
所以我们要求的总共只有
a
i
a_i
ai种情况
那么这又如何求出呢,跑一次膜意义下最短路就行了
令
d
i
s
[
i
]
dis[i]
dis[i]表示余数为
i
i
i的最小数,那么连边很显然
对于
a
i
(
i
>
=
2
)
a_i(i>=2)
ai(i>=2),我们连接
d
i
s
[
i
]
dis[i]
dis[i]和
(
d
i
s
[
i
]
+
a
i
)
m
o
d
  
a
1
(dis[i]+a_i)\mod {a_1}
(dis[i]+ai)moda1,边权为
a
i
a_i
ai
完事,开跑
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 5e5+7;
const ll INF = 1e13;
ll mod,dis[maxn],n,bmax,bmin,val[200];
bool cmp1(ll a,ll b){return a<b;}
int vis[maxn];
void spfa(){
for(int i=0;i<mod;i++)dis[i]=INF;
dis[0]=0;vis[0]=1;
queue<ll>q;q.push(0);
while(!q.empty()){
ll f1=q.front();q.pop();vis[f1]=0;
for(int i=2;i<=n;i++){
ll to=(f1+val[i])%mod;
if(dis[to]>dis[f1]+val[i]){
dis[to]=dis[f1]+val[i];
if(!vis[to])vis[to]=1,q.push(to);
}
}
}
}
int main(){
scanf("%lld%lld%lld",&n,&bmin,&bmax);bmin--;
for(ll i=1;i<=n;i++){
scanf("%lld",&val[i]);
}
sort(val+1,val+1+n,cmp1);
mod=val[1];
spfa();
ll ans=0;
for(int i=0;i<mod;i++){
if(dis[i]>bmax)continue;
else if(dis[i]>=bmin&&dis[i]<=bmax){
ans+=(bmax-dis[i])/mod+1;
}
else {
ans+=(bmax-dis[i])/mod-(bmin-dis[i])/mod;
}
}
cout<<ans<<endl;
return 0;
}