区间gcd
多次查询区间查询
\(\gcd(a_L,a_{L+1},...,a_R)\)
使用 ST表 可以 \(O(n*\log n)\) 预处理出区间的gcd,然后 \(O(1)\) 查询。
性质
令 \(f(L,R)=\gcd(a_L,a_{L+1},...,a_R)\)
从 \(f(L,L),f(L,L+1),f(L,L+2),...,f(L,R)\),一定是单调不升的一个序列。
从上可得,最多有 \(logx\) 种 \(\gcd\) 的取值。
故可以通过 \(O(n*\log n)\) 的复杂度预处理出所有的区间 \(\gcd\)。
例题
F-小辰刚学 gcd_牛客小白月赛81
多次查询集合 \(S=U_{i=L}^R \{\gcd_{j=i}^Ra_j\}\) 的大小
代码
| C++ |
|---|
| #include <bits/stdc++.h>
using namespace std;
// 2024 OneWan
long long a[600005];
vector<pair<long long, int>> f[600005];
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int n, m;
cin >> n >> m;
for (int i = 1 ; i <= n ; i++) {
cin >> a[i];
}
for (int i = 1 ; i <= n ; i++) {
long long g = a[i];
f[i].emplace_back(g, i);
for (auto &[preg, idx] : f[i - 1]) {
long long tg = __gcd(g, preg);
if (tg != g) {
f[i].emplace_back(tg, idx);
g = tg;
}
if (g == 1) break;
}
}
while (m--) {
int L, R;
cin >> L >> R;
int ans = 0;
for (int i = 0 ; i < int(f[R].size()) ; i++) {
// 此时区间 [L, R] 的 gcd 均为 f[R][i].first
// L 属于区间 [f[R][i + 1].second + 1, f[R][i].second]
// 当 i = f[R].size() - 1 时, L = 1
if (L <= f[R][i].second) {
ans++;
}
}
cout << ans << "\n";
}
return 0;
}
|
K-同学聚会_第十一届"图灵杯"NEUQ-ACM程序设计竞赛
求 \(\dfrac{\sum_{L=1}^{n}\sum_{R=L}^n(R-L+1)*\gcd_{i=L}^Ra_i}{\dfrac{n*(n+1)}{2}}\)
代码
| C++ |
|---|
| #include <bits/stdc++.h>
using namespace std;
// 2024 OneWan
int a[200005];
vector<pair<int, int>> f[200005];
const long long mod = 998244353;
long long qpow(long long a, long long b) {
long long res = 1;
while (b) {
if (b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int n;
cin >> n;
for (int i = 1 ; i <= n ; i++) {
cin >> a[i];
}
for (int i = n ; i >= 1 ; i--) {
int g = a[i];
f[i].emplace_back(g, i);
for (auto &[preg, idx] : f[i + 1]) {
int tg = __gcd(g, preg);
if (tg != g) {
f[i].emplace_back(tg, idx);
g = tg;
}
if (g == 1) break;
}
}
long long ans = 0;
for (int i = 1 ; i <= n ; i++) {
for (int j = 0 ; j < int(f[i].size()) ; j++) {
// 此时区间 [i, R] 的 gcd 均为 f[i][j].first
// R 属于区间 [f[i][j].second, f[i][j + 1].second - 1]
// 当 j = f[i].size() - 1 时, R = n;
int L = f[i][j].second, R, g = f[i][j].first;
if (j < int(f[i].size()) - 1) {
R = f[i][j + 1].second - 1;
} else {
R = n;
}
long long res = (1LL * (L + R) * (R - L + 1) / 2 - 1LL * (i - 1) * (R - L + 1) % mod + mod) % mod * g % mod;
ans = (ans + res) % mod;
}
}
cout << ans * qpow(1LL * n * (n + 1) / 2 % mod, mod - 2) % mod << "\n";
return 0;
}
|