P2371 [国家集训队] 墨墨的等式
题目描述
墨墨突然对等式很感兴趣,他正在研究 ∑ i = 1 n a i x i = b \sum_{i=1}^n a_ix_i=b ∑i=1naixi=b 存在非负整数解的条件,他要求你编写一个程序,给定 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] 可以使等式存在非负整数解。
输入格式
第一行三个整数 n , l , r n,l,r n,l,r。
第二行 n n n 个整数 a 1 … n a_{1\dots n} a1…n。
输出格式
一行一个整数,表示有多少 b ∈ [ l , r ] b\in[l,r] b∈[l,r] 可以使等式存在非负整数解。
输入输出样例 #1
输入 #1
2 5 10
3 5
输出 #1
5
说明/提示
对于 20 % 20\% 20% 的数据, n ≤ 5 n \le 5 n≤5, r ≤ 10 r \le 10 r≤10。
对于 40 % 40\% 40% 的数据, n ≤ 10 n \le 10 n≤10, r ≤ 1 0 6 r \le 10^6 r≤106。
对于 100 % 100\% 100% 的数据, 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。
思路:
这个题代码倒不难写,就同余最短路的板题,但是公式不好推,判断这题用同余最短路和建边不太好推导。
∑ i = 1 n a i x i = b \sum_{i=1}^n a_ix_i=b i=1∑naixi=b
假设我们的
∑
i
=
1
n
a
i
x
i
=
t
\sum_{i=1}^n a_ix_i=t
∑i=1naixi=t 有一组非负整数解
{
x
1
,
x
2
,
x
3
…
…
x
n
−
1
,
x
n
}
\{x_1,x_2, x_3……x_{n-1},x_n\}
{x1,x2,x3……xn−1,xn},那我们必定能获得第其他组非负整数解的方法是什么呢?我们可以发现,当我加上
k
k
k 个
a
p
(
p
∈
[
1
,
n
]
)
a_p(p∈[1, n])
ap(p∈[1,n]) 的时候,一定会满足这个公式,因为就相当于
a
p
a_p
ap 的系数加上了
k
k
k,也就是(假设
p
=
1
p = 1
p=1)
{
x
1
+
k
,
x
2
,
x
3
…
…
x
n
−
1
,
x
n
}
\{x_1 + k,x_2, x_3……x_{n-1},x_n\}
{x1+k,x2,x3……xn−1,xn}。所以我们就推出来了一个结论:
∑
i
=
1
n
a
i
x
i
=
t
+
k
×
a
p
(
p
∈
[
1
,
n
]
)
\sum_{i=1}^n a_ix_i=t + k×a_p(p∈[1, n])
i=1∑naixi=t+k×ap(p∈[1,n])
最后我们在计算结果的是
[
0
,
r
]
−
[
0
,
l
−
1
]
=
[
l
,
r
]
[0, r] - [0, l - 1] = [l, r]
[0,r]−[0,l−1]=[l,r],因为我们同余最短路求出来的是
d
i
s
t
dist
dist 同余组最小值,如果说
(
l
−
1
)
>
d
i
s
t
(l - 1) > dist
(l−1)>dist 的话我用
d
i
s
t
−
(
l
−
1
)
x
+
1
\frac{dist - (l - 1)}{ x + 1}
x+1dist−(l−1) 得到的就是我在范围
[
0
,
l
−
1
]
[0, l - 1]
[0,l−1] 里
d
i
s
t
+
k
×
x
dist + k ×x
dist+k×x 能计算出的数的数量。
[
0
,
r
]
[0, r]
[0,r] 也同理,
d
i
s
t
−
r
x
+
1
\frac{dist - r}{x + 1}
x+1dist−r。然后将两者相减得到
[
l
,
r
]
[l, r]
[l,r]。为什么一定要这么做呢,因为我这个同余最短路求出来的是满足同余组要求的最小值,如果说
l
=
6
l = 6
l=6,而同余组余
1
1
1 的组(上界
x
=
4
x = 4
x=4)结果是
5
5
5, 那我
5
5
5 就比
6
6
6 小,换句话说就是同余最短路求出来的数只与
m
o
d
x
mod x
modx 的
x
x
x 有关,不会被
l
,
r
l,r
l,r约束,所以求出最小值还要去得到符合这个区间的数也就是
t
+
k
×
a
p
t + k×a_p
t+k×ap ,但是挨个遍历如果 r 过大时间复杂度会过高,所以用前缀和的思想,求一遍范围在
[
0
,
l
−
1
]
[0, l - 1]
[0,l−1] 里的,一遍
[
0
,
r
]
[0, r]
[0,r] 的,做差得到结果
[
l
,
r
]
[l, r]
[l,r] 的数量。
AC code:
#include <iostream>
#include <climits>
#include <limits>
#include <vector>
#include <queue>
typedef unsigned long long ull;
typedef long long ll;
typedef long double ld;
typedef std::pair<ll, ll> PII;
#define rep(i, n) for(ll i = 0; i < n; i++)
#define Rep(i, len, n) for(ll i = len; i < n; i++)
#define MAX_INT 0x7fffffff
#define MIN_INT 0x80000000
const ll INF = std::numeric_limits<ll>::max();
ll n, l, r, x = INF;
std::vector<std::vector<PII>> e;
std::vector<ll> a, dist;
std::vector<bool> vis;
std::priority_queue<PII, std::vector<PII>, std::greater<PII>> q;
inline void Dijkstra() {
q.push({0, 0});
dist[0] = 0;
while(!q.empty()) {
auto [val, u] = q.top();
q.pop();
if(vis[u]) continue;
vis[u] = true;
for(const auto&[v, w] : e[u]) {
if(dist[v] > dist[u] + w) {
dist[v] = dist[u] + w;
q.push({dist[v], v});
}
}
}
}
int main(void) {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr), std::cout.tie(nullptr);
std::cin >> n >> l >> r;
a.resize(n);
rep(i, n) {
std::cin >> a[i];
if(a[i] != 0) x = std::min(x, a[i]);
}
dist.resize(x, INF);
vis.resize(x, false);
e.resize(x);
rep(i, x) {
rep(j, n) {
// 自己不用跟自己建边
if(a[j] != x) e[i].push_back({(i + a[j]) % x, a[j]});
}
}
Dijkstra();
ll ans_l = 0, ans_r = 0;
rep(i, x) {
if(dist[i] <= (l - 1)) ans_l += ((l - 1) - dist[i]) / x + 1;
if(dist[i] <= r) ans_r += (r - dist[i]) / x + 1;
}
std::cout << ans_r - ans_l << '\n';
return 0;
}