题意:给定一个n的排列,询问L到R之间任意两个数的gcd之和
以下是解题报告的思路,解题报告看了n久才看懂, 记一下,以免以后忘记了~~
对于L到R之间的一个数A,如果数A的一个因子B在L到R之间出现了K次,则结果加上K*F[B]。例如4有因子2,出现了K次,加上K*F[2]。
当然A有因子B,则A有B的因子C,又要加上K*F[C]。 假如A有因子2,除去A之外,L到R还有有1个因子2,1个因子1,其实gcd之和为2。所以要因子要乘上1个系数F[i]。
F[i]怎么求呢,假设B有n个因子,B1, B2...Bn,则F[B] + F[B1] + F[B2] + ... + F[Bn] = B。这样的话同时有K个B1,B2,... Bn时gcd之和就加上了K*B。
F[1]=1,然后可暴力求出F[i]。其实F[i] = phi[i]。
于是统计L到R之间的每个因子出现的次数即可求出答案。
因为询问太多,所以要离线处理每个询问。当把A加进区间时,对A的每个因子求答案,然后改变因子出现次数,删除时差不多。
//#pragma comment(linker, "/STACK:10240000000000,10240000000000")
#include<cstdio>
#include<cstring>
#include<climits>
#include<string>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<queue>
#include<vector>
#include<stack>
#include<map>
#include<set>
#include<stdlib.h>
using namespace std;
typedef long long ll;
void io_in_data() { freopen("data.in", "r", stdin);freopen("data1.out", "w", stdout); }
const int MAXN = 20007;
vector<int> divisor[MAXN];
int f[MAXN], a[MAXN], L[MAXN], R[MAXN], cnt[MAXN], qid[MAXN], M;
ll ans[MAXN];
void getdivisor() {
int i, j;
for(i = 1; i < MAXN; i++) for(j = i; j < MAXN; j += i) divisor[j].push_back(i);
}
void getf() {
for(int i = 1; i < MAXN; i++) {
int t = i;
int j, sz = divisor[i].size();
for(j = 0; j < sz; j++) t -= f[divisor[i][j]];
f[i] = t;
}
}
int qcmp(int x, int y) {
int px = L[x] / M; //避免L相差不大然后R相差很大的情况
int py = L[y] / M;
if(px != py) return px < py;
return R[x] < R[y];
}
ll erase(int x) {
int i, y, sz = divisor[x].size();
ll ret = 0;
for(i = 0; i < sz; i++) {
y = divisor[x][i];
ret += 1LL*f[y]*(--cnt[y]);
}
return ret;
}
ll insert(int x) {
int i, y, sz = divisor[x].size();
ll ret = 0;
for(i = 0; i < sz; i++) {
y = divisor[x][i];
ret += 1LL*f[y]*(cnt[y]++);
}
return ret;
}
void solve(int n, int q) {
int i;
for(i = 1; i <= q; i++) qid[i] = i;
M = (int)sqrt(n*1.0);
sort(qid+1, qid+1+q, qcmp);
memset(cnt, 0, sizeof(cnt));
int l = 0, r = 0;
ll now = 0;
for(i = 1; i <= q; i++) {
int id = qid[i];
int nl = L[id], nr = R[id];
while(l < nl) {
now -= erase(a[l]);
l++;
}
while(l > nl) {
l--;
now += insert(a[l]);
}
while(r < nr) {
r++;
now += insert(a[r]);
}
while(r > nr) {
now -= erase(a[r]);
r--;
}
ans[id] = now;
}
}
int main() {
getdivisor();
getf();
int T, id = 0;
scanf("%d", &T);
while(T--) {
int i, n, q;
scanf("%d", &n);
for(i = 1; i <= n; i++) scanf("%d", a+i);
scanf("%d", &q);
for(i = 1; i <= q; i++) scanf("%d%d", L+i, R+i);
solve(n, q);
printf("Case #%d:\n", ++id);
for(i = 1; i <= q; i++) printf("%I64d\n", ans[i]);
}
return 0;
}