【BZOJ3529】【莫比乌斯反演 + 树状数组】[Sdoi2014]数表

Description

    有一张N×m的数表,其第i行第j列(1 < =i < =礼,1 < =j < =m)的数值为
能同时整除i和j的所有自然数之和。给定a,计算数表中不大于a的数之和。

Input

    输入包含多组数据。
    输入的第一行一个整数Q表示测试点内的数据组数,接下来Q行,每行三个整数n,m,a(|a| < =10^9)描述一组数据。

Output

    对每组数据,输出一行一个整数,表示答案模2^31的值。

Sample Input

2
4 4 3
10 10 5

Sample Output

20
148

HINT

1 < =N.m < =10^5  , 1 < =Q < =2×10^4

Source

Round 1 Day 1

【分析】

其实是个比较水的题目。

 $\sum\limits_{i <  = n\ {\rm{ j <  = m}}}^{} { F(gcd(i,j))} $其中gcd(i,j) <= a,F(i),为i的因子和。
我们可以将公式变形,先忽略gcd(i, j) <= a这个条件。
$\sum\limits_{i = 1}^{\min (n,m)} {F(i) \times num(i)} $      num(i)为i在 gcd(i,j) (i <=n, j <= m)中出现的次数。
易得:
$num(i) = \sum\limits_{i|d} {\mu (\frac{d}{i}) \times \left\lfloor {\frac{n}{d}} \right\rfloor }  \times \left\lfloor {\frac{m}{d}} \right\rfloor $
然后原式可以化为:
$\sum\limits_{i = 1}^{\min (n,m)} {F(i)}  \times \sum\limits_{i|d} {\mu (\frac{d}{i}) \times \left\lfloor {\frac{n}{d}} \right\rfloor }  \times \left\lfloor {\frac{m}{d}} \right\rfloor$
换元:
$\sum\limits_{d = 1}^{\min (n,m)} {\left\lfloor {\frac{m}{d}} \right\rfloor \left\lfloor {\frac{m}{d}} \right\rfloor } \sum\limits_{i|d} {F(i)\mu (\frac{d}{i})} $
然后再把$\sum\limits_{i|d} {F(i)\mu (\frac{d}{i})} $预处理出来,对询问以a为关键字排序,用树状数组记录一段的和,前面用分块,然后就可以做了。
  1 /*

  2 唐代杜牧的《遣怀》

  3 落魄江南载酒行,楚腰纤细掌中轻。

  4 十年一觉扬州梦,赢得青楼薄幸名。 

  5 */

  6 #include <cstdio>

  7 #include <cstring>

  8 #include <algorithm>

  9 #include <cmath>

 10 #include <queue>

 11 #include <vector>

 12 #include <iostream>

 13 #include <string>

 14 #include <ctime>

 15 #include <map>

 16 #include <set> 

 17 #define LOCAL

 18 long long MOD = 1000000000 + 7;

 19 const int MAXM = 1000 * 1000 + 10;

 20 const int MAXN = 100000 + 10;

 21 using namespace std;

 22 //输入输出优化 

 23 int read(){

 24     int x = 0, flag = 1;

 25     char ch = getchar();

 26     while (ch < '1' || ch >'9') {if (ch == '-') flag = -1; ch = getchar();}

 27     while (ch >= '0' && ch <= '9') {x = x * 10 + (ch - '0'); ch = getchar();}

 28     return x * flag;

 29 }

 30 struct FF{

 31     int order;//order表示该num对应的gcd值

 32     int num;

 33     bool operator < (const FF &b)const{

 34         return num < b.num;

 35     }

 36 }F[MAXN];

 37 struct QUERY{

 38     int l, r, a, order;

 39     bool operator < (const QUERY &b)const{

 40         return a < b.a;

 41     }

 42 }q[MAXN];

 43 int mu[MAXN], prime[MAXN];

 44 int g[MAXN], C[MAXN], Q, Ans[MAXN];

 45 

 46 int lowbit(int x){return x & -x;}

 47 void add(int x, int val){

 48     while (x <= 100000){

 49         C[x] += val;

 50         x += lowbit(x);

 51     }

 52     return;

 53 }

 54 int sum(int x){

 55     int cnt = 0;

 56     while (x > 0){

 57         cnt += C[x];

 58         x -= lowbit(x);

 59     }

 60     return cnt;

 61 }

 62 void prepare(){

 63     memset(prime, 0, sizeof(prime));

 64     memset(C, 0, sizeof(C));

 65     

 66     mu[1] = 1;

 67     for (int i = 2;i <= 100000; i++){

 68         if (!prime[i]){

 69             prime[++prime[0]] = i;

 70             mu[i] = -1;

 71         }

 72         for (int j = 1; j <= prime[0]; j++){

 73             if (i * prime[j] > 100000) break;

 74             prime[i * prime[j]] = 1;

 75             if (i % prime[j] == 0){

 76                 mu[i * prime[j]] = 0;

 77                 break;

 78             }else mu[i * prime[j]] = -mu[i];

 79         }

 80     }

 81     //F[i]代表i的因数和

 82     F[1].num = F[1].order = 1;

 83     for (int i = 2; i <= 100000; i++){

 84         int cnt = 0;

 85         for (long long j = 1; j * (long long)j <= (long long)i; j++){

 86             if (j * j == i){cnt += j; break;}

 87             if (i % j != 0) continue;

 88             cnt += j + (i / j);

 89         }

 90         F[i].num = cnt;

 91         F[i].order = i;

 92     }

 93     sort(F + 1, F + 1 + 100000);

 94     //for (int i = 1; i <= 100; i++) printf("%d\n", mu[i]);

 95 }

 96 void init(){

 97     scanf("%d", &Q);

 98     for (int i = 1; i <= Q; i++){

 99         int l = read(), r = read(), a = read();

100         q[i].l = l; q[i].r = r;

101         q[i].a = a; q[i].order = i;

102     }

103     sort(q + 1, q + 1 + Q);

104 }

105 //直接回答第x个询问

106 int query(int x){

107     int cnt = 0;

108     for (int i = 1; i <= min(q[x].l, q[x].r); i++){

109         int t = min(q[x].l / (q[x].l / i), q[x].r / (q[x].r / i));

110         cnt += (q[x].l / i) * (q[x].r / i) * (sum(t) - sum(i - 1));

111         i = t;

112     }

113     return cnt;

114 }

115 void work(){

116     int pos = 1;//表示a现在的大小

117     for (int i = 1; i <= Q; i++){

118         while (F[pos].num <= q[i].a && pos <= 100000){

119             for (int j = 1; j * F[pos].order <= 100000; j++)

120                 add(j * F[pos].order, F[pos].num * mu[j]);

121             pos++;

122         }

123         Ans[q[i].order] = query(i);

124     }

125     for (int i = 1; i <= Q; i++) printf("%d\n", Ans[i] & 0x7fffffff);

126 }

127 

128 int main(){

129     int T;

130 

131      prepare();

132      init();

133      work();

134      return 0;

135 }

136  
View Code

 

 

(k=1nakbk)2(k=1na2k)(k=1nb2k)

你可能感兴趣的:(树状数组)