BZOJ 2154 crash的数字表格
Description
今天的数学课上,Crash小朋友学习了最小公倍数(Least Common Multiple)。对于两个正整数a和b,LCM(a, b)表示能同时被a和b整除的最小正整数。例如,LCM(6, 8) = 24。回到家后,Crash还在想着课上学的东西,为了研究最小公倍数,他画了一张N*M的表格。每个格子里写了一个数字,其中第i行第j列的那个格子里写着数为LCM(i, j)。一个4*5的表格如下: 1 2 3 4 5 2 2 6 4 10 3 6 3 12 15 4 4 12 4 20 看着这个表格,Crash想到了很多可以思考的问题。不过他最想解决的问题却是一个十分简单的问题:这个表格中所有数的和是多少。当N和M很大时,Crash就束手无策了,因此他找到了聪明的你用程序帮他解决这个问题。由于最终结果可能会很大,Crash只想知道表格里所有数的和mod 20101009的值。
Input
输入的第一行包含两个正整数,分别表示N和M。
Output
输出一个正整数,表示表格中所有数的和mod 20101009的值。
1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 #include<algorithm> 5 6 #define maxn 10000001 7 8 #define mod 20101009 9 10 using namespace std; 11 12 long long mu[maxn],prime[maxn],mx,mi; 13 14 long long f[maxn],ans=0; 15 16 bool is_prime[maxn]; 17 18 long long sum(long long x,long long y){return (x*(x+1)/2%mod)*(y*(y+1)/2%mod)%mod;} 19 20 void mu_choice() 21 { 22 long long b=0; 23 mu[1]=1; 24 for(long long i=2;i<=mx;i++) 25 { 26 if(!is_prime[i])mu[i]=-1,prime[++b]=i; 27 long long j=1,t=2*i; 28 while(j<=b&&t<=mx) 29 { 30 is_prime[t]=1; 31 if(i%prime[j]==0) 32 { 33 mu[t]=0; 34 break; 35 } 36 mu[t]=-mu[i]; 37 t=prime[++j]*i; 38 } 39 } 40 for(long long i=1;i<=mi;i++) 41 f[i]=(f[i-1]+(i*i%mod*mu[i]))%mod; 42 } 43 44 long long F(long long n,long long m) 45 { 46 long long re=0,last; 47 if(n>m)swap(n,m); 48 for(long long i=1;i<=n;i=last+1) 49 { 50 last=min(n/(n/i),m/(m/i)); 51 re=(re+(f[last]-f[i-1])*sum(n/i,m/i)%mod)%mod; 52 } 53 return re; 54 } 55 56 int main() 57 { 58 #ifndef ONLINE_JUDGE 59 freopen("2154.in","r",stdin); 60 freopen("2154.out","w",stdout); 61 #endif 62 long long n,m; 63 scanf("%lld%lld",&n,&m); 64 mx=max(n,m); 65 mi=min(n,m); 66 mu_choice(); 67 long long last; 68 for(long long i=1;i<=mi;i=last+1) 69 { 70 last=min(n/(n/i),m/(m/i)); 71 ans+=((last-i+1)*(last+i)/2%mod*F(n/i,m/i)%mod); 72 ans%=mod; 73 } 74 printf("%lld",(ans+mod)%mod); 75 return 0; 76 }
BZOJ 2693 jzptab
Description
Input
一个正整数T表示数据组数
接下来T行 每行两个正整数 表示N、M
Output
T行 每行一个整数 表示第i组数据的结果
显然用上一题的公式去套是不现实的。。O(Tn)的复杂度已经无法阻挡了。。
我们继续考虑优化。
首先列出式子
然后我们设D=i*d,则有:
看起来只要求出SUM()后面的sigma的前缀和求一求,分块就可以辣~\(≧▽≦)/~
前缀和怎么求呢。。设h[p]=后面那一坨。。
则如果p是一个质数,则h[p]=p-p*p。
如果 p 是多个素数的一次项的积
显然 h 是积性的。
如果 p 存在得一个质因子的指数大于1,那么它新增的每一个因子的 μ 值都是0,没有意义,只有统计时D变成了原来的 j 倍。
所以 此时 h( p ) = h( i ) * j
之后前缀和处理一下就行了。
1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 #include<algorithm> 5 6 #define mod 100000009 7 8 #define maxn 10000001 9 10 using namespace std; 11 12 int mu[maxn],b=0,prime[maxn]; 13 14 long long h[maxn]; 15 16 bool is_prime[maxn]; 17 18 void mu_choice() 19 { 20 h[1]=mu[1]=1; 21 for(long long i=2;i<maxn;i++) 22 { 23 if(!is_prime[i])prime[++b]=i,h[i]=(i-i*i%mod)%mod; 24 long long j=1,t=2*i; 25 while(j<=b&&t<=maxn-1) 26 { 27 is_prime[t]=1; 28 if(i%prime[j]==0) 29 { 30 h[t]=h[i]*prime[j]%mod; 31 break; 32 } 33 h[t]=h[i]*h[prime[j]]%mod; 34 t=prime[++j]*i; 35 } 36 } 37 for(int i=2;i<maxn;i++) 38 h[i]=(h[i-1]+h[i])%mod; 39 } 40 41 long long sum(long long x,long long y){return (x*(x+1)/2%mod)*(y*(y+1)/2%mod)%mod;} 42 43 int main() 44 { 45 int T; 46 scanf("%d",&T); 47 mu_choice(); 48 long long last; 49 while(T--) 50 { 51 long long n,m,ans=0; 52 scanf("%lld%lld",&n,&m); 53 if(n>m)swap(n,m); 54 for(long long i=1;i<=n;i=last+1) 55 { 56 last=min(n/(n/i),m/(m/i)); 57 ans=(ans+sum(n/i,m/i)*(h[last]-h[i-1])%mod)%mod; 58 } 59 printf("%lld\n",(ans+mod)%mod); 60 } 61 return 0; 62 }
大家凑合看吧。。