BZOJ 3529 [Sdoi2014]数表

3529: [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


 

  对于这种套路题,似乎已无什么新意,在此粗粗列举一下。

  BZOJ 1968  

  BZOJ 2005 

  BZOJ 2301(1101) 再加上区间的加加减减

  BZOJ 3309  f(n)指n所含质因子的最大幂指数

  BZOJ 2820

  BZOJ 4407

  BZOJ 2693 

  BZOJ 3994 

  我们可以大致发现,这种题目通常就是先展开,再收缩,最后算一下替代品。中间十足充分的利用了性质,就可以做出一道题。做这种题常常会令人心旷神怡,只是因为太难出,所以似乎并不能常常出现在OI赛场上。

  但是,对于这种题目,如果能充分理解并掌握,那么你就会很好地理解整数到底意味着什么,整除到底意味着什么。如果这时候还能有一本《组合数学》打辅助,数论应该就没有什么问题了。

  在这样的推导之中,我们常常用到这个式子

  这个式子的意义极其重大,因为如果会推这个式子,还知道积性函数是什么,那么就能秒掉不少题了。

  对于这道题,我们就是要算出。但是,题目要求,第i个询问中算数的,这就需要离线了。我们把询问排个序,保证之后就可以搞了。随即把所有的预处理出来,然后把sigma数组排个序,之后处理每个询问。这样,每个sigma就会一个个的加入我们的统计结果,毕竟是调和级数。因为在加入时只会影响d的倍数,而我们查询时需要计算前缀和,这就可使用树状数组。

  至此,这道题就算完了。因为mod那个神奇的数,所以只需要在最后ans&0x7fffffff就好了。

 1 /**************************************************************
 2     Problem: 3529
 3     User: Doggu
 4     Language: C++
 5     Result: Accepted
 6     Time:3488 ms
 7     Memory:3308 kb
 8 ****************************************************************/
 9  
10 #include <cstdio>
11 #include <cstring>
12 #include <algorithm>
13 const int N = 20000 + 50;
14 const int RANGE = 100000;
15 struct DATUM {
16     int n, m, a, id;
17     bool operator<(const DATUM &rhs) const {
18         return a<rhs.a;
19     }
20 }data[N];
21 std::pair< int, int > sigma[RANGE+100];
22 int ans[N], mu[RANGE+100], minpa[RANGE+100], BOUND;
23  
24 int prime[10000], ptot;
25 bool vis[RANGE+100];
26 void init_sigma() {
27     mu[1]=1;sigma[1].first=1;
28     for( int i = 2; i <= BOUND; i++ ) {
29         if(!vis[i]) prime[++ptot]=i, mu[i]=-1, sigma[i].first=i+1, minpa[i]=i;
30         for( int j = 1; j <= ptot; j++ ) {
31             if((long long)i*prime[j]>BOUND) break;
32             vis[i*prime[j]]=1;
33             if(i%prime[j]==0) {
34                 mu[i*prime[j]]=0;
35                 if(i==minpa[i]) sigma[i*prime[j]].first=sigma[i].first+minpa[i]*prime[j];
36                 else sigma[i*prime[j]].first=sigma[i/minpa[i]].first*sigma[prime[j]*minpa[i]].first;
37                 minpa[i*prime[j]]=minpa[i]*prime[j];
38                 break;
39             }
40             mu[i*prime[j]]=-mu[i];
41             sigma[i*prime[j]].first=sigma[i].first*sigma[prime[j]].first;
42             minpa[i*prime[j]]=prime[j];
43         }
44     }
45     for( int i = 1; i <= BOUND; i++ ) sigma[i].second=i;
46 }
47  
48 int aa[RANGE+100];
49 void add(int pos,int val) {for( int x=pos; x<=BOUND; x+=x&-x)aa[x]+=val;}
50 int query(int pos,int val=0) {for( int x=pos; x; x-=x&-x )val+=aa[x];return val;}
51  
52 int last=1;
53 void solve(int a) {
54     while(sigma[last].first<=a) {
55         int val = sigma[last].first, pos = sigma[last].second;
56         for( int i = pos; i <= BOUND; i+=pos ) add(i,val*mu[i/pos]); 
57         last++;
58     }
59 }
60  
61 int main() {
62     int T;scanf("%d",&T);
63     for( int i = 1; i <= T; i++ ) {
64         scanf("%d%d%d",&data[i].n,&data[i].m,&data[i].a), data[i].id=i;
65         if(data[i].n>data[i].m) std::swap(data[i].n,data[i].m);
66         BOUND=std::max(BOUND,data[i].n);
67     }
68     init_sigma();
69     std::sort(data+1,data+T+1);
70     std::sort(sigma+1,sigma+BOUND+1);
71     for( int i = 1, n, m; i <= T; i++ ) {
72         n = data[i].n, m = data[i].m;solve(data[i].a);
73         if(n>m) std::swap(n,m);
74         for( int a = 1, ed; a <= n; a=ed+1 ) {
75             ed=std::min(n/(n/a),m/(m/a));
76             ans[data[i].id]+=(n/a)*(m/a)*(query(ed)-query(a-1));
77         }
78     }
79     for( int i = 1; i <= T; i++ ) printf("%d\n",ans[i]&0x7fffffff);
80     return 0;
81 }
posted @ 2017-08-24 17:09 Doggu 阅读(...) 评论(...) 编辑 收藏