HDU4676 Sum Of Gcd
2013-08-15 22:07
260 查看
题意:给定一个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的每个因子求答案,然后改变因子出现次数,删除时差不多。
以下是解题报告的思路,解题报告看了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; }
相关文章推荐
- [数论 反演 && 莫队] hdu4676 . Sum Of Gcd
- hdu4676 Sum Of Gcd
- [莫比乌斯反演+莫队] HDU4676 Sum Of Gcd
- 【HDU4676】Sum Of Gcd-莫队算法+欧拉函数
- HDU4676 Sum Of Gcd (数论 + 分块)
- HDU 5381 The sum of gcd
- hdu5381(2015多校8)--The sum of gcd(线段树)
- 2015 Multi-University Training Contest 8 hdu 5381 The sum of gcd
- hdu 5381 The sum of gcd(线段树)
- HDU 5381 The sum of gcd
- HDU 5381 The sum of gcd
- HDU4767_Sum Of Gcd
- HDU 5381 The sum of gcd 询问区间内所有子区间的GCD和 [莫队算法]
- hdu 4676 Sum Of Gcd 莫队+phi反演
- hdu5381 The sum of gcd
- HDU - 4676 Sum Of Gcd 莫比乌斯+莫队
- HDOJ 5381 The sum of gcd 莫队算法
- Sum Of Gcd(hdu 4676)
- HDU 4676 Sum Of Gcd(欧拉函数求区间gcd之和+分块算法)
- HDU 5381 The sum of gcd