題意:
給定區間,求所有子區間gcd之和。區間gcd定義爲區間中所有數的gcd。
思路:
從一個點開始,朝某個方向一直求gcd,會形成一個非增序列,且下降不超過32次。(每次下降至少減半)
所以在線的思路就是用線段樹,每個節點代表一個區間,sum保存這個區間所有子區間的gcd和,同時保存兩個狀態集合,分別是從左端點開始的連續區間,和從右端點開始的連續區間。參考了 Dora 的代碼。
同時,離線的思路是:
在線code
const int N = 10000 + 5;
typedef long long LL;
int gcd(int a, int b) {
for (int t; b; t = a, a = b, b = t % b);
return a;
}
struct part {
int cnt, g;
};
struct node {
vector<part> L, R;
LL sum;
void upd(const node& rhs) {
sum += rhs.sum;
for (int i = 0; i < R.size(); ++ i) {
int ng = R[i].g;
for (int j = 0; j < rhs.L.size(); ++ j) {
ng = gcd(ng, rhs.L[j].g);
sum += (LL)(ng) * R[i].cnt * rhs.L[j].cnt;
}
}
for (int i = 0; i < rhs.L.size(); ++ i) Insert(L, rhs.L[i]);
vector<part> tmp(rhs.R);
for (int i = 0; i < R.size(); ++ i) Insert(tmp, R[i]);
R.swap(tmp);
}
static void Insert(vector<part>& vec, const part& x) {
int ng = gcd(vec.back().g, x.g);
if ( ng == vec.back().g ) {
vec.back().cnt += x.cnt;
} else {
vec.push_back( (part) {x.cnt, ng} );
}
}
};
node tree[N*4];
#define lc o<<1
#define rc o<<1|1
void print(const node& x, int l, int r) {
cout << "node " << l << ' ' << r << endl;
cout << " sum: " << x.sum << endl;
cout << " L: ";for(auto i:x.L) cout << "(" << i.cnt << " , " << i.g << ") ";cout << endl;
cout << " R: ";for(auto i:x.R) cout << "(" << i.cnt << " , " << i.g << ") ";cout << endl;
cout << endl;
}
void build(int o, int l, int r) {
if ( l != r ) {
int m = (l + r) >> 1;
build(lc, l, m);
build(rc, m+1, r);
tree[o] = tree[lc];
tree[o].upd(tree[rc]);
} else {
int x;
scanf("%d", &x);
tree[o].sum = x;
vector<part>().swap(tree[o].L);
vector<part>().swap(tree[o].R);
tree[o].L.push_back( (part) { 1, x } );
tree[o].R.push_back( (part) { 1, x } );
}
//print(tree[o], l, r);
}
int qL, qR;
node qres;
void query(int o, int l, int r) {
if ( qL <= l && r <= qR ) {
if ( l == qL )
qres = tree[o];
else
qres.upd(tree[o]);
return;
}
int m = (l + r) >> 1;
if ( qL <= m ) query(lc, l, m);
if ( qR > m ) query(rc, m+1, r);
}
int main() {
#ifdef _LOCA_ENV_
freopen("input.in", "r", stdin);
#endif // _LOCA_ENV
int t;
scanf("%d", &t);
while (t--) {
int n, q;
scanf("%d", &n);
build(1, 1, n);
scanf("%d", &q);
rep(i, 1, q) {
scanf("%d%d", &qL, &qR);
//cout << "query " << qL << ' ' << qR << endl;
query(1, 1, n);
printf("%I64d\n", qres.sum);
}
}
return 0;
}