分析
40pts
一道很巧妙的题目,果然不愧是往届集训队的题目,在网上看了一下,似乎是一道同余最短路(雾)。
首先看到题目,可能会以为很难的数论,根本看不出来是一道最短路。分析一下题目,首先可以想到一个暴力做法,直接完全背包。可以推出方程:
f
[
j
]
=
f
[
j
]
∣
f
[
j
−
a
[
i
]
]
f[j]=f[j] | f[j - a[i]]
f[j]=f[j]∣f[j−a[i]]
这是一个只能得
40
p
t
s
40pts
40pts的做法,这里就不再赘述。
100pts
进入正题。我们需要更优的算法,于是我们开始解剖这个式子。
因为每个
x
i
x_i
xi都是正整数,所以当我们得到一个
b
b
b时,我们同样也可以得到
b
+
x
i
b+x_i
b+xi,对于每一个
a
i
a_i
ai都是如此,也就转化为了找小于
m
i
n
(
a
i
)
min(a_i)
min(ai)有哪些数,再在当前每一个可以得到的数上加上k倍的
a
i
a_i
ai判断是否是在
[
l
,
r
]
[l,r]
[l,r]里。
设
M
i
n
n
Minn
Minn为
m
i
n
(
a
i
)
min(a_i)
min(ai)
为了找到这些小于
M
i
n
n
Minn
Minn基准数,我们可以用最短路。因为我们知道对于每一个
i
i
i,我们都可以加一个
a
[
j
]
a[j]
a[j]使他成为一个数
i
+
a
[
j
]
i+a[j]
i+a[j],然而
i
+
a
[
j
]
i+a[j]
i+a[j]的基准数又是
(
i
+
a
[
j
]
)
m
o
d
(
M
i
n
n
)
(i+a[j])mod(Minn)
(i+a[j])mod(Minn),于是我们就可以连一条有向边,从
i
i
i变成
(
i
+
a
[
j
]
)
m
o
d
(
M
i
n
n
)
(i+a[j])mod(Minn)
(i+a[j])mod(Minn),他的代价是
a
[
j
]
a[j]
a[j],也就是他需要加
a
[
j
]
a[j]
a[j]才能变成以
(
i
+
a
[
j
]
)
m
o
d
(
M
i
n
n
)
(i+a[j])mod(Minn)
(i+a[j])mod(Minn)为基准数的一个数。我们不难知道,对于每一个
i
i
i,从
0
0
0走到
i
i
i就是他最小代价。
最后,我们已经找到了每一个基准数,就可以很简单的手推一下 r r r与 l − 1 l-1 l−1的个数,答案就是相减。
code
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#define int long long
using namespace std;
const int MAXN = 5e5 + 5;
int n,l,r,a[MAXN],cnt,num,dis[MAXN];
int Minn = 0x3f3f3f3f,head[MAXN];
bool vis[MAXN];
struct node{
int val,to,Next;
}edge[MAXN * 12];
struct data{
int u,w;
data(){}
data(int U,int W){u = U,w = W;}
friend bool operator < (data x,data y) {return x.w > y.w;}
};
priority_queue<data> Q;
void Add(int x,int y,int z) {
edge[++num].Next = head[x];
edge[num].to = y;
edge[num].val = z;
head[x] = num;
}
void dijkstra() {
memset(dis,0x3f,sizeof(dis));
Q.push(data(0,0));
dis[0] = 0;
while(!Q.empty()) {
data f = Q.top();
Q.pop();
if(vis[f.u]) continue;
vis[f.u] = 1;
for(int i = head[f.u];i;i = edge[i].Next) {
int To = edge[i].to;
if(dis[To] > dis[f.u] + edge[i].val) {
dis[To] = dis[f.u] + edge[i].val;
Q.push(data(To,dis[To]));
}
}
}
}
int solve(int x) {
int ans = 0;
for(int i = 0;i < Minn;i ++)
if(dis[i] <= x) ans += (x - dis[i]) / Minn + 1;
return ans;
}
signed main() {
scanf("%lld %lld %lld",&n,&l,&r);
for(int i = 1;i <= n;i ++) {
int x;
scanf("%lld",&x);
if(!x) continue;
Minn = min(Minn,x);
a[++cnt] = x;
}
for(int i = 0;i < Minn;i ++)
for(int j = 1;j <= cnt;j ++)
if(a[j] != Minn)
Add(i,(i + a[j]) % Minn,a[j]);
dijkstra();
printf("%lld",solve(r) - solve(l - 1));
return 0;
}