题意
给你一个数组,询问 [ l , r ] [l,r] [l,r]之间所有数的 gcd \gcd gcd以及所有等于这个数的区间数。
题解
区间 gcd \gcd gcd可以用线段树和ST表解决,而所有区间gcd等于某个数的个数则需要求解一番。首先我们可以确定,数越多,得出来的gcd肯定会越小,即 gcd ( a 1 , a 2 , . . . , a i ) ≤ gcd ( a 1 , a 2 , . . . , a i , a i + 1 ) \gcd(a_1,a_2,...,a_i)\le\gcd(a_1,a_2,...,a_i,a_{i+1}) gcd(a1,a2,...,ai)≤gcd(a1,a2,...,ai,ai+1),也就是说,gcd的前缀和是单调递减的,利用这个性质,可以在 n l o g n nlogn nlogn的时间内预处理出所有g的区间个数。
AC代码:
#include <bits/stdc++.h>
#define IO ios::sync_with_stdio(0)
#define _ff(i, a, b) for (int i = a; i <= b; ++i)
#define _f(i, a, b) for (int i = a; i < b; ++i)
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
ll a[N], f[N][30], n;
map<ll, ll> mp;
void getST() {
_ff(i, 1, n) f[i][0] = a[i];
_ff(j, 1, 30) _ff(i, 1, n) {
if (i + (1<<j) - 1 <= n) f[i][j] = __gcd(f[i][j - 1], f[i + (1<<(j - 1))][j - 1]);
else break;
}
}
ll query(int l, int r) {
ll k = (ll)log2(1.0 * (r - l + 1));
return __gcd(f[l][k], f[r - (1<<k) + 1][k]);
}
void getMp() {
mp.clear();
_ff(i, 1, n) {
ll g = a[i], j = i;
while (j <= n) {
ll l = j, r = n;
while (l < r) {
ll mid = (l + r + 1) >> 1;
if (query(l, mid) == g) l = mid;
else r = mid - 1;
}
mp[g] += l - j + 1;
j = l + 1;
g = query(i, j);
}
}
}
void solve() {
cin >> n;
_ff(i, 1, n) cin >> a[i];
getST();
getMp();
int q, l, r; cin >> q;
while (q--) {
cin >> l >> r;
ll g = query(l, r);
cout << g << " " << mp[g] << endl;
}
}
int main() {
IO;
int T; cin >> T;
_ff(i, 1, T) {
cout << "Case #" << i << ":\n";
solve();
}
}