题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5381
题目大意:给出一个长度为 n 的数组a,现在有q次询问,每次询问给出一个区间 [L,R],要你求出这个区间内所有子区间的gcd之和,即求。
题目思路:根据gcd的性质,以L为起点,终点小于等于R的子区间所得到的不同的gcd个数不超过个。所以对于一开始给出的每一个值a[i],我们就可以维护出以这个点为起点,终点从 i 到 n 的子区间的gcd值。
既然这样,我们就可以维护一棵线段树,线段树中下标为 i 的结点表示以 i 为终点的所有子区间的gcd之和。同时倒着将a数组内的值更新到线段树中。在更新的同时我们还要将相同的gcd值进行合并,记录每一段的信息即可。
在查询答案时,我们可以用一个vector来存储查询区间的左端点为L的所有区间,当更新到这个端点时,我们就对其所在的区间进行查询,由于我们是倒着更新a数组内的值,所以这个区间的右端点的值一定是已经被更新了的。
时间复杂度是
具体实现看代码:
#include <bits/stdc++.h>
#define fi first
#define se second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pb push_back
#define MP make_pair
#define lowbit(x) x&-x
#define clr(a) memset(a,0,sizeof(a))
#define _INF(a) memset(a,0x3f,sizeof(a))
#define FIN freopen("in.txt","r",stdin)
#define IOS ios::sync_with_stdio(false)
#define fuck(x) cout<<"["<<#x<<" "<<(x)<<"]"<<endl
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int>pii;
typedef pair<ll, ll>pll;
const int MX = 1e4 + 5;
int n, q, _;
vector<pii>p[MX];
int a[MX];
ll sum[MX << 2], lazy[MX << 2], ans[MX];
int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}
void push_up(int rt) {
sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
void push_down(int l, int r, int rt) {
if (lazy[rt]) {
int m = (l + r) >> 1;
lazy[rt << 1] += lazy[rt];
lazy[rt << 1 | 1] += lazy[rt];
sum[rt << 1] += lazy[rt] * (m - l + 1);
sum[rt << 1 | 1] += lazy[rt] * (r - m);
lazy[rt] = 0;
}
}
void build(int l, int r, int rt) {
sum[rt] = lazy[rt] = 0;
if (l == r) return;
int m = (l + r) >> 1;
build(lson); build(rson);
push_up(rt);
}
void update(int L, int R, int d, int l, int r, int rt) {
if (L <= l && r <= R) {
sum[rt] += (ll)(r - l + 1) * d;
lazy[rt] += d;
return;
}
push_down(l, r, rt);
int m = (l + r) >> 1;
if (L <= m) update(L, R, d, lson);
if (R > m) update(L, R, d, rson);
push_up(rt);
}
ll query(int L, int R, int l, int r, int rt) {
if (L <= l && r <= R) return sum[rt];
push_down(l, r, rt);
ll res = 0;
int m = (l + r) >> 1;
if (L <= m) res += query(L, R, lson);
if (R > m) res += query(L, R, rson);
return res;
}
int g[MX], l[MX], r[MX];
int Merage(int cnt) {
int j = 0;
for (int i = 0; i < cnt; j++, i++) {
g[j] = g[i]; l[j] = l[i]; r[j] = r[i];
while (g[i] == g[i + 1]) {
l[j] = min(l[j], l[i + 1]);
r[j] = max(r[j], r[i + 1]);
i++;
}
}
return j;
}
int main() {
// FIN;
for (scanf("%d", &_); _; _--) {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]), p[i].clear();
build(1, n, 1);
scanf("%d", &q);
for (int i = 1; i <= q; i++) {
int L, R;
scanf("%d%d", &L, &R);
p[L].pb(MP(R, i));
}
int cnt = 0;
for (int i = n; i >= 1; i--) {
for (int j = 0; j < cnt; j++) {
int x = gcd(g[j], a[i]);
g[j] = x;
}
g[cnt] = a[i]; l[cnt] = r[cnt] = i; cnt++;
cnt = Merage(cnt);
for (int j = 0; j < cnt; j++) update(l[j], r[j], g[j], 1, n, 1);
for (auto now : p[i])
ans[now.se] = query(i, now.fi, 1, n, 1);
}
for (int i = 1; i <= q; i++)
printf("%lld\n", ans[i]);
}
return 0;
}