询问好搞 线段树即可
问题在于如何统计区间个数 需要注意到以i开头的区间每次gcd发生变化gcd的值都至少除以二(最小因子为2) 所以最多变化logn次 只需快速找到这logn个变化点即可快速计算每个gcd值对应的区间个数并用map保存
查找过程可以利用选段树 不断查找由i开始使当前gcd值小于目标值的位置 对于每一个i至多logn次 线段树操作复杂度logn 总复杂度nlogn^2
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<map>
#define SF scanf
#define PF printf
using namespace std;
typedef long long LL;
inline int read() {
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') { if(ch == '-') f = -1; ch = getchar(); }
while(ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
const int MAXN = 100000;
const int MOD = 1e9 + 7;
const int INF = 1234567890;
int n;
int gcd(int a, int b) {
while(b) {
int r = a % b;
a = b;
b = r;
}
return a;
}
int A[MAXN+10];
struct Seg_Tree {
int g[MAXN*4+10];
void build(int i, int l, int r) {
if(l == r) { g[i] = A[l]; return ; }
int mid = (l+r) >> 1;
build(i*2, l, mid);
build(i*2+1, mid+1, r);
g[i] = gcd(g[i*2], g[i*2+1]);
}
int query(int i, int l, int r, int L, int R) {
if(L <= l && r <= R) return g[i];
int mid = (l+r) >> 1;
int ls = 0, rs = 0;
if(L <= mid) ls = query(i*2, l, mid, L, R);
if(R > mid) rs = query(i*2+1, mid+1, r, L, R);
if(!ls) swap(ls, rs);
return gcd(ls, rs);
}
int search(int i, int l, int r, int L, int R, int cur, int &GCD) {
if(L <= l && r <= R) {
if(gcd(g[i], GCD) < cur) {
if(l == r) return l;
else {
int mid = (l+r) >> 1, ls = 0, rs = 0;
ls = search(i*2, l, mid, L, R, cur, GCD);
if(ls) return ls;
rs = search(i*2+1, mid+1, r, L, R, cur, GCD);
return rs;
}
}
else {
GCD = gcd(GCD, g[i]);
return 0;
}
}
int mid = (l+r) >> 1, ls = 0, rs = 0;
if(L <= mid) ls = search(i*2, l, mid, L, R, cur, GCD);
if(ls) return ls;
if(R > mid) rs = search(i*2+1, mid+1, r, L, R, cur, GCD);
return rs;
}
} seg;
map <int, LL> M;
int main() {
int T = read();
for(int kase = 1; kase <= T; kase++) {
M.clear();
n = read();
for(int i = 1; i <= n; i++) A[i] = read();
seg.build(1, 1, n);
for(int i = 1; i <= n; i++) {
int g = seg.query(1, 1, n, i, n);
for(int Now = i, cur = A[i]; Now <= n; ) {
if(g == cur) { M[g] += n - Now + 1; break; }
int GCD = A[i];
int Nex = seg.search(1, 1, n, i, n, cur, GCD);
M[cur] += Nex - Now;
Now = Nex;
cur = gcd(A[Now], cur);
}
}
printf("Case #%d:\n", kase);
int Q = read();
for(int i = 1; i <= Q; i++) {
int l = read(), r = read();
int g = seg.query(1, 1, n, l, r);
PF("%d %I64d\n", g, M[g]);
}
}
return 0;
}