HDU 6102 - GCDispower | 2017 Multi-University Training Contest 6
个人感觉题解的复杂度很玄,参不透,有没有大佬讲解一下- -
/* HDU 6102 - GCDispower [ 数论,树状数组] | 2017 Multi-University Training Contest 6 题意: 给定排列 a[N], M 组 L,R 求解 ∑ [ L <= i < j < k <= R ] [ GCD(a[i], a[j]) == a[k] ] * a[k] 限制:N, M <= 1e5 分析: 数论角度一般考虑枚举 k,由于是区间询问,且贡献有可加性,考虑对每个L,将[L, R-1] 推到 [L, R] 故对于每一个R, 枚举 a[R] 倍数 a[i] (i < R) 再对每一个 a[i] , 求得满足 i < j < R && GCD(a[i], a[j]) == a[R] 的个数 即 GCD(a[i]/a[R], a[j]/a[R]) == 1 的个数 此时对于 L ∈ [1,i-1] 的区间,贡献 = 所得个数 * a[R] , 这部分用区间更新可以完成 求 GCD(a[i]/a[R], a[j]/a[R]) == 1 的 j 的个数,用容斥原理 a[j]是a[R]的倍数的总个数 - a[j]与a[i]不互质的个数 = a[j]是a[R]的倍数的总个数 - a[j] 是 a[i] 的 1个质因子之积的倍数的个数 + a[j] 是 a[i] 的 2个质因子之积的倍数的个数 ... + (-1)^k * a[j] 是 a[i] 的 k个质因子之积的倍数的个数 所以预处理每个数所有质因子之积,然后容斥的参数 μ = -1^(k) 可以用莫比乌斯函数 具体处理时,可以维护每个质因子之积的倍数,每处理一个 a[i] , 就将它的每个质因子之积的倍数个数+1 复杂度: 预处理 O(n+n^1.5) 枚举 R 和 a[i] 均摊 O(nlog(n)), 枚举 a[i] 的因子容斥 O(n^0.5) 区间查询,更新 O(log(n)) 总复杂度 : O(n + n^1.5 + T * n * log(n)*(n^0.5 + log(n))) 不过由于枚举因子时枚举的是非完全平方数,不足n^0.5,可能优化下来就 n*log(n)^2 了(???) */ #include <bits/stdc++.h> using namespace std; #define LL long long const int N = 100005; bool notp[N]; int prime[N], pnum, mu[N]; vector<int> fac[N]; void Mobius() { memset(notp, 0, sizeof(notp)); mu[1] = 1; for (int i = 2; i < N; i++) { if (!notp[i]) prime[++pnum] = i, mu[i] = -1; for (int j = 1; prime[j]*i < N; j++) { notp[prime[j]*i] = 1; if (i%prime[j] == 0) { mu[prime[j]*i] = 0; break; } mu[prime[j]*i] = -mu[i]; } } for (int i = 1; i < N; i++) for (int j = 1; j*j <= i; j++) { if (j*j == i && mu[j]) fac[i].push_back(j); else if (i%j == 0) { if (mu[j]) fac[i].push_back(j); if (mu[i/j]) fac[i].push_back(i/j); } } } int t, n, m, a[N], vis[N]; struct Query { int l, id; }; vector<Query> Q[N]; LL ans[N]; LL c[N]; void modify(int x, int num) { if (x == 0) return; while (x <= n) c[x] += num, x += x&-x; } LL sum(int x){ LL s = 0; while (x) s += c[x], x -= x&-x; return s; } int cnt[N]; void addCnt(int x) { for (auto& y : fac[x]) cnt[y]++; } void solve(int l, int x, int k) { int num = 0; for (auto& y : fac[x]) num += mu[y] * cnt[y]; modify(1, k*num); modify(l+1, -k*num); addCnt(x); } vector<int> mul; bool cmp(int a, int b) { return a > b; } void init() { memset(vis, 0, sizeof(vis)); memset(cnt, 0, sizeof(cnt)); memset(c, 0, sizeof(c)); for (int i = 0; i < N; i++) Q[i].clear(); } int main() { Mobius(); scanf("%d", &t); while (t--) { init(); scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) scanf("%d", &a[i]); for (int i = 1; i <= m; i++) { int l, r; scanf("%d%d", &l, &r); Q[r].push_back(Query{l, i}); } for (int i = 1; i <= n; i++) { mul.clear(); for (int j = 2*a[i]; j <= n; j += a[i]) if (vis[j]) mul.push_back(vis[j]); sort(mul.begin(), mul.end(), cmp); for (auto & l : mul) solve(l, a[l]/a[i], a[i]); for (int j = 0; j <= n/a[i]; j++) cnt[j] = 0; vis[a[i]] = i; for (auto& x : Q[i]) ans[x.id] = sum(x.l); } for (int i = 1; i <= m; i++) printf("%lld\n", ans[i]); } }
我自倾杯,君且随意