HDU 4676 Sum Of Gcd 【莫队 + 欧拉】

任意门:http://acm.hdu.edu.cn/showproblem.php?pid=4676

Sum Of Gcd

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)
Total Submission(s): 908    Accepted Submission(s): 438


Problem Description
Given you a sequence of number a 1, a 2, ..., a n, which is a permutation of 1...n.
You need to answer some queries, each with the following format:
Give you two numbers L, R, you should calculate sum of gcd(a[i], a[j]) for every L <= i < j <= R.
 
Input
First line contains a number T(T <= 10),denote the number of test cases.
Then follow T test cases.
For each test cases,the first line contains a number n(1<=n<= 20000).
The second line contains n number a 1,a 2,...,a n.
The third line contains a number Q(1<=Q<=20000) denoting the number of queries.
Then Q lines follows,each lines contains two integer L,R(1<=L<=R<=n),denote a query.
 
Output
For each case, first you should print "Case #x:", where x indicates the case number between 1 and T.
Then for each query print the answer in one line.
 
Sample Input
1
5
3 2 5 4 1
3
1 5
2 4
3 3
 
Sample Output
Case #1:
11
4
0
 
Source

题意概括:

给出 1~N 的一个排列,M次查询,每次查询 L ~ R 内 GCD( ai, aj )  [ L <= i < j <= R ] 的总和。

解题思路:

又是涉及 GCD 又是 涉及区间查询,头有点大。

首先莫队处理区间查询,其次欧拉函数解决GCD问题。

根据:

 

那么用 gcd( ai, aj) 代替上式的 n,我们可以得到:

问题就转换成了求 d 的欧拉函数,其实 d 是有很多重复的,那么我们只要统计出当前查询区间【L,R】内 d 出现的次数然后乘上其欧拉函数值,所求的的答案就是区间GCD的总和。

欧拉函数值和对原序列的每一项的因数分解预处理时搞定。

AC code:

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <algorithm>
 4 #include <cstring>
 5 #include <vector>
 6 #include <cmath>
 7 #define INF 0x3f3f3f3f
 8 #define LL long long
 9 using namespace std;
10 
11 const int MAXN = 2e4+20;
12 int unit, a[MAXN], N, M, cnt[MAXN];
13 LL ans[MAXN], phi[MAXN];
14 vector<int>factor[MAXN];
15 
16 struct Query
17 {
18     int l, r, idx;
19     friend bool operator < (const Query & a, const Query & b){
20         int x1 = a.l/unit, x2 = b.l/unit;
21         if(x1 != x2) return x1 < x2;
22         return a.r < b.r;
23     }
24 }Q[MAXN];
25 
26 void init()
27 {
28     for(int i = 1; i < MAXN; i++){      //分解因子
29         for(int j = i; j < MAXN; j+=i)
30             factor[j].push_back(i);
31     }
32 
33     phi[1] = 1;                         //欧拉函数
34     for(int i = 2; i < MAXN; i++){
35         phi[i] = i;
36     }
37     for(int i = 2; i < MAXN; i++){
38         if(phi[i] == i){
39             for(int j = i; j < MAXN; j+=i)
40                 phi[j] = phi[j]/i*(i-1);
41         }
42         //puts("zjy");
43     }
44 }
45 
46 LL add(int x)
47 {
48     LL res = 0;
49     for(auto d : factor[x]) res+=cnt[d]*phi[d];
50     for(auto d : factor[x]) cnt[d]++;
51     return res;
52 }
53 
54 LL del(int x)
55 {
56     LL res = 0;
57     for(auto d : factor[x]) cnt[d]--;
58     for(auto d : factor[x]) res+=cnt[d]*phi[d];
59     return -res;
60 }
61 
62 
63 void solve()
64 {
65     memset(cnt, 0, sizeof(cnt));
66     int L = 1, R = 0;
67     LL cur = 0;
68     for(int i = 1; i <= M; i++){
69         while( L < Q[i].l) cur += del(a[L++]);
70         while( L > Q[i].l) cur += add(a[--L]);
71         while( R < Q[i].r) cur += add(a[++R]);
72         while( R > Q[i].r) cur += del(a[R--]);
73         ans[Q[i].idx] = cur;
74         //puts("zjy");
75     }
76 }
77 
78 int main()
79 {
80     int T_Case, Cas = 0;
81     init();
82     //puts("zjy");
83     scanf("%d", &T_Case);
84     while(T_Case--){
85         scanf("%d", &N);
86         for(int i = 1; i <= N; i++) scanf("%d", &a[i]);
87         scanf("%d", &M);
88         for(int i = 1; i <= M; i++){
89             scanf("%d %d", &Q[i].l, &Q[i].r);
90             Q[i].idx = i;
91         }
92         unit = sqrt(N);
93         sort(Q+1, Q+1+M);
94         solve();
95         printf("Case #%d:\n", ++Cas);
96         for(int i = 1; i <= M; i++) printf("%lld\n", ans[i]);
97     }
98     return 0;
99 }

猜你喜欢

转载自www.cnblogs.com/ymzjj/p/10390012.html