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
4 4 3
10 10 5
Sample Output
148
HINT
1 < =N.m < =10^5 , 1 < =Q < =2×10^4
Source
对于这种套路题,似乎已无什么新意,在此粗粗列举一下。
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 ",ans[i]&0x7fffffff); 80 return 0; 81 }