第一问是没有修改的线段树,第二问暴力预处理,因为gcd的结果不会很多
在预处理阶段需要把每个区间的gcd相等的数量储存起来(用map容器),在一个序列例如:12467,枚举左区间L直到n此处时间为O(n),l=1时寻找右区间随着r的增大gcd单调不增,在某个区域内gcd相等想到用二分查找时间是O(logn),再加上线段树查找用到时间O(logn),总时间是O(n*logn*longn)会超时,所以需要更优。
解决办法:左区间固定时利用线段树找到gcd减小的区间,从l到gcd减小的区间gcd相等。
#include<map> #include<stdio.h> #include<string> #include<iostream> #include<algorithm> using namespace std; #define LL long long const int N = 4e5 + 10; int T, n, m, g[N], a[N], l, r, q, cas = 0; map<int, LL> M; int gcd(int x, int y) { return x%y ? gcd(y, x%y) : y; } void build(int x, int l, int r) { if (l == r) scanf("%d", &g[x]), a[l] = g[x]; else { int mid = l + r >> 1; build(x << 1, l, mid); build(x << 1 | 1, mid + 1, r); g[x] = gcd(g[x << 1], g[x << 1 | 1]); } } int get(int x, int l, int r, int ll, int rr) { if (ll <= l&&r <= rr) return g[x]; int mid = l + r >> 1; int x1 = 0, x2 = 0; if (ll <= mid) x1 = get(x << 1, l, mid, ll, rr); if (rr > mid) x2 = get(x << 1 | 1, mid + 1, r, ll, rr); return gcd(min(x1, x2), max(x1, x2)); } bool find(int x, int l, int r, int ll, int rr, int u, int &v) { if (ll <= l && r <= rr) { if (gcd(v, g[x]) < u) { if (l == r) { q = l; return true; } else { int mid = l + r >> 1; if (find(x<<1, l, mid, ll, rr, u, v)) return true; if (find(x<<1|1, mid + 1, r, ll, rr, u, v)) return true; } } else { v = gcd(v, g[x]); return false; } } else { int mid = l + r >> 1; if (ll <= mid&&find(x<<1, l, mid, ll, rr, u, v)) return true; if (rr > mid&& find(x<<1|1, mid + 1, r, ll, rr, u, v)) return true; return false; } } int main() { scanf("%d", &T); while (T--) { M.clear(); scanf("%d", &n); build(1, 1, n); for (int i = 1, j, k; i <= n; i++) { int kk = get(1, 1, n, i, n); for (k = i, j = a[i]; k <= n;) { if (kk == j) { M[j] += n - k + 1; break; } int gg = a[i]; find(1, 1, n, i, n, j, gg); M[j] += q - k; k = q; j = gcd(a[q], j); } } scanf("%d", &m); printf("Case #%d: ", ++cas); while (m--) { scanf("%d%d", &l, &r); int x = get(1, 1, n, l, r); printf("%d %lld ", x, M[x]); } } return 0; }