基本都是半年前做的题。。。印象不深,所以从头开始推式子写代码。
除了《一个人的数论》是3周前刚做的整个思路还在于是就粘了之前的代码。
都是套路,就那么几样:初等和式变换(所谓改变枚举顺序),数论分块,前缀和优化,设置新变量改变表达方式,以及及时预处理。
莫比乌斯反演的基本形式是$f(n)=sumlimits_{i|n} g(i)$等价于$g(n)=sumlimits_{i|n} mu(i) f(frac{n}{i})$
然而其实最常用的就是$[n]=sumlimits_{i|n} mu(i)$
然而隔了半年再做这些题,几乎都没有$1A$,我是不是要废了。。。
所有除法均向下取整。
YY的GCD:
$Description:$
神犇YY虐完数论后给傻×kAc出了一题给定N, M,求1<=x<=N, 1<=y<=M且gcd(x, y)为质数的(x, y)有多少对kAc这种傻×必然不会了,于是向你来请教……
$T = 10000, N, M <= 10000000$
$sumlimits_{i=1}^{n} sumlimits_{j=1}^{m} [gcd(i,j)=p]$
$=sumlimits_{p} sumlimits_{i=1}^{frac{n}{p}} sumlimits_{j=1}^{frac{m}{p}} [gcd(i,j)]$
$=sumlimits_{p} sumlimits_{i=1}^{frac{n}{p}} sumlimits_{j=1}^{frac{m}{p}} sumlimits_{d|i and d|j} mu(d)$
$=sumlimits_{p} sumlimits_{d} mu(d) frac{n}{pd} frac{m}{pd}$
设$x=pd$
$=sumlimits_{x} frac{n}{x} frac{m}{x} sumlimits_{d|x and frac{x}{d} is prime} mu(d)$
最后一个和式可以调和级数预处理,其余数论分块。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 #define S 10000005 5 int p[S],pc,mu[S],cal[S];;char np[S]; 6 signed main(){ 7 int n,m,t;cin>>t;mu[1]=1; 8 for(int i=2;i<S;++i){ 9 if(!np[i])p[++pc]=i,mu[i]=-1; 10 for(int j=1,x;x=i*p[j],j<=pc&&x<S;++j) 11 if(i%p[j])np[x]=1,mu[x]=-mu[i]; 12 else {np[x]=1;mu[x]=0;break;} 13 } 14 for(int i=1;i<=pc;++i)for(int j=1,x=p[i];x<S;++j,x+=p[i])cal[x]+=mu[j]; 15 for(int i=1;i<S;++i)cal[i]+=cal[i-1]; 16 while(t--){cin>>n>>m;ll ans=0;if(n>m)n^=m^=n^=m; 17 for(int i=1,l,N,M;N=n/i,M=m/i,i<=n;i=l+1)l=min(n/N,m/M),ans+=(0ll+cal[l]-cal[i-1])*N*M; 18 cout<<ans<<endl; 19 } 20 }
数表:
$Description:$
有一张N×m的数表,其第i行第j列(1 < =i < =礼,1 < =j < =m)的数值为能同时整除i和j的所有自然数之和。给定a,计算数表中不大于a的数之和。
$n,m le 10^5, Q le 2 imes 10^4$
$a$很烦人,考虑如何处理。
其实就是有些数有贡献,有些数暂时没有贡献。
离线下来按照$a$排序,让数依次产生贡献即可。
设$f(x)=sumlimits_{i|x} i$。如果小于$a$则为$0$。
题目要求:$sumlimits_{i=1}^{n} sumlimits_{j=1}^{m} f(gcd(i,j))$
$=sumlimits_{p} f(p) sumlimits_{i=1}^{frac{n}{p}} sumlimits_{j=1}^{frac{m}{p}} [gcd(i,j)]$
$=sumlimits_{p} f(p) sumlimits_{d} mu(d) frac{n}{pd} frac{m}{pd}$
$=sumlimits_{x} frac{n}{x} frac{m}{x} sumlimits_{d|x} mu(d) f(frac{x}{d})$
后面前缀和预处理(是一边离线一边预处理,用树状数组维护即可),前面继续数论分块。
1 #include<bits/stdc++.h> 2 using namespace std; 3 priority_queue<pair<int,int> >q; 4 #define S 100005 5 #define u unsigned 6 int mu[S],pc,p[S],qn;u int f[S],ans[S],t[S];char np[S]; 7 struct QS{ 8 int n,m,o,a; 9 friend bool operator<(QS a,QS b){return a.a<b.a;} 10 }Q[S]; 11 void add(int p,int w){for(;p<S;p+=p&-p)t[p]+=w;} 12 u int ask(int p,u int a=0){for(;p;p^=p&-p)a+=t[p];return a;} 13 void Add(int p,int w){for(int i=1;i*p<S;++i)add(i*p,mu[i]*w);} 14 int main(){mu[1]=1; 15 for(int i=2;i<S;++i){ 16 if(!np[i])mu[p[++pc]=i]=-1; 17 for(int j=1,x;j<=pc&&(x=i*p[j])<S;++j) 18 if(i%p[j])np[x]=1,mu[x]=-mu[i]; 19 else{mu[x]=0;np[x]=1;break;} 20 }for(int i=1;i<S;++i)for(int j=i;j<S;j+=i)f[j]+=i; 21 for(int i=1;i<S;++i)q.push(make_pair(-f[i],i)); 22 q.push(make_pair(-1000000007,0)); 23 cin>>qn;for(int i=1;i<=qn;++i)cin>>Q[i].n>>Q[i].m>>Q[i].a,Q[i].o=i; 24 sort(Q+1,Q+1+qn); 25 for(int i=1;i<=qn;++i){ 26 int n=Q[i].n,m=Q[i].m,o=Q[i].o,N,M,l;if(n>m)n^=m^=n^=m; 27 while(q.top().first>=-Q[i].a)Add(q.top().second,-q.top().first),q.pop(); 28 for(int i=1;N=n/i,M=m/i,i<=n;i=l+1)l=min(n/N,m/M),ans[o]+=(ask(l)-ask(i-1))*N*M; 29 }for(int i=1;i<=qn;++i)cout<<(ans[i]&(1u<<31)-1)<<endl; 30 }
DZY Loves Math:
$Description:$
对于正整数n,定义f(n)为n所含质因子的最大幂指数。例如f(1960)=f(2^3 * 5^1 * 7^2)=3, f(10007)=1, f(1)=0。
给定正整数a,b,求sigma(sigma(f(gcd(i,j)))) (i=1..a, j=1..b)。$T le 10000, 1 le a,b le 10^7$
$f$可以线筛。只要记录每个数最后一次出现的因子出现次数,最多的出现次数就行。
$sumlimits_{i=1}^{n} sumlimits_{j=1}^{m} f(gcd(i,j))$
$=sumlimits_{p} f(p) sumlimits_{d} mu(d) frac{n}{pd} frac{m}{pd}$
设$x=pd$
$=sumlimits_{x} frac{n}{x} frac{m}{x} frac{d|x} f(d) mu(frac{x}{d})$
问题在于最后一个和式。数据范围很大$O(n ln n)$过不去,考虑优化。
存在$O(n ln ln n)$的做法,不难想(不断提出最后一种质因子),不再赘述。
从含义上考虑,$mu$在某一个因子次数大于$1$时值就会变成$0$,而最后一个和式相当于在把$x$的所有质因子分成两部分给$f$和$mu$。
而$mu$每种因子最多只能接受$1$个,其余都要给$f$。
如果一个数,它存在一种质因子出现次数与其它质因子出现次数不同,那么不管其它因子如何分配,这个因子一定会使$mu$正一次负一次,贡献抵消,总值为$0$
剩下的情况就是所有质因子出现次数相同。考虑和式的值。
我们钦定某一种质因子分配给$mu$了$0$个还是$1$个。可以发现如果$f$值相同的情况下正负依旧抵消。
唯一不同的就是如果所有质因子都给$mu$分配了$1$个,那么此时$f$的值减小了$1$。
再考虑此时$mu$的正负,可知如果因子种数有奇数个$f=1$,否则$f=-1$。
这样就解决了最后一个和式,前面还是数论分块。
说了这么多,不如打个表。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define S 10000007 4 int p[S],lt[S],pc;char np[S];short mc[S],mt[S],tt[S],f[S]; 5 int main(){ 6 for(int i=2;i<S;++i){ 7 if(!np[i])p[++pc]=i,lt[i]=mt[i]=mc[i]=tt[i]=1; 8 for(int j=1,x;j<=pc&&(x=p[j]*i)<S;++j) 9 if(i%p[j]){ 10 np[x]=lt[x]=1,tt[x]=tt[i]+1,mt[x]=mt[i],mc[x]=mc[i]; 11 if(mt[x]==1)mc[x]++; 12 } 13 else{ 14 lt[x]=lt[i]+1;tt[x]=tt[i]+1;np[x]=1; 15 if(lt[x]>mt[i])mt[x]=lt[x],mc[x]=1; 16 else if(lt[x]==mt[i])mt[x]=lt[x],mc[x]=mc[i]+1; 17 else mt[x]=mt[i],mc[x]=mc[i]; 18 break; 19 } 20 }for(int i=2;i<S;++i)f[i]=f[i-1]+(tt[i]==mt[i]*mc[i])*(mc[i]&1?1:-1); 21 int t,n,m,N,M;long long ans=0;cin>>t;while(t--){ 22 cin>>n>>m;if(n>m)n^=m^=n^=m; 23 for(int i=1,l;N=n/i,M=m/i,i<=n;i=l+1)l=min(n/N,m/M),ans+=1ll*N*M*(f[l]-f[i-1]); 24 cout<<ans<<endl;ans=0; 25 } 26 }
约数个数和:
$Description:$
设$d(x)$为$x$的约数个数,给定$N$、$M$,求$sumlimits_{i=1}^{N} sumlimits_{j=1}^{M} d(ij)$
$1<=N, M<=50000,1<=T<=50000$
结论题。这就很难受了。
$d(nm)=sumlimits_{x|n}sumlimits_{y|m} [gcd(x,y)]$
证明网上到处都是。记结论吧。
$sumlimits_{i=1}^{n} sumlimits_{j=1}^{m} d(ij)$
$=sumlimits_{i=1}^{n} sumlimits_{j=1}^{m} sumlimits_{x=1}^{i} sumlimits_{y=1}^{j} [gcd(i,j)]$
$=sumlimits_{i=1}^{n} sumlimits_{j=1}^{m} sumlimits_{x=1}^{i} sumlimits_{y=1}^{j} sumlimits_{d|x and d|y} mu(d)$
$=sumlimits_{d} mu(d) sumlimits_{x=1}^{frac{n}{d}} frac{n}{dx} sumlimits_{y=1}^{frac{m}{d}}frac{m}{dy}$
后面的两个和式分别只与$frac{n}{d}$和$frac{m}{d}$有关,设为函数$f(x)$
$=sumlimits_{d} mu(d) f(frac{n}{d}) f(frac{m}{d})$
预处理$f$,数论分块。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define S 50005 4 int mu[S],p[S],pc,f[S];char np[S]; 5 int main(){mu[1]=1; 6 for(int i=2;i<S;++i){ 7 if(!np[i])mu[i]=-1,p[++pc]=i; 8 for(int j=1,x;x=i*p[j],j<=pc&&x<S;++j) 9 if(i%p[j])mu[x]=-mu[i],np[x]=1; 10 else {np[x]=1;mu[x]=0;break;} 11 } 12 for(int i=1;i<S;++i)for(int j=1,l,x;x=i/j,j<=i;j=l+1)l=i/x,f[i]+=x*(l-j+1); 13 for(int i=1;i<S;++i)mu[i]+=mu[i-1]; 14 int t,n,m;cin>>t;while(t--){ 15 long long ans=0;cin>>n>>m;if(n>m)n^=m^=n^=m; 16 for(int i=1,l,N,M;N=n/i,M=m/i,i<=n;i=l+1)l=min(n/N,m/M),ans+=(0ll+mu[l]-mu[i-1])*f[N]*f[M]; 17 cout<<ans<<endl; 18 } 19 }
数字表格:
$Description:$
Doris刚刚学习了fibonacci数列。用f[i]表示数列的第i项,那么
f[0]=0
f[1]=1
f[n]=f[n-1]+f[n-2],n>=2
Doris用老师的超级计算机生成了一个n×m的表格,第i行第j列的格子中的数是f[gcd(i,j)],其中gcd(i,j)表示i,j的最大公约数。Doris的表格中共有n×m个数,她想知道这些数的乘积是多少。答案对$10^9+7$取模。
奇怪的题目?为什么是累乘而不是求和啊?
先别着急上原根。。。好像指数就可以直接求和的样子。。。
$prodlimits_{i=1}^{n} prodlimits_{j=1}^{m} fib(gcd(i,j))$
$=prodlimits_{p} fib(p)^{ sumlimits_{i=1}^{n} sumlimits_{j=1}^{m} [gcd(i,j)] }$
$=prodlimits_{p} fib(p)^{ sumlimits_{d} mu(d) frac{n}{pd} frac{m}{pd} }$
$=prodlimits_{x} (prodlimits_{d|x} fib(d)^{ mu(frac{x}{d} ) } )^{frac{n}{x} frac{m}{x}}$
还是数论分块,加和乘没有本质区别。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define S 1000005 4 #define mod 1000000007 5 int pw(int b,int t,int a=1){for(;t;t>>=1,b=1ll*b*b%mod)if(t&1)a=1ll*a*b%mod;return a;} 6 int fib[S],f[S],mu[S],p[S],pc,iv[S];char np[S]; 7 int main(){mu[1]=f[1]=fib[1]=f[0]=1; 8 for(int i=2;i<S;++i){ 9 if(!np[i])p[++pc]=i,mu[i]=-1; 10 for(int j=1,x;j<=pc&&(x=p[j]*i)<S;++j) 11 if(i%p[j])np[x]=1,mu[x]=-mu[i]; 12 else {np[x]=1;mu[x]=0;break;} 13 }for(int i=2;i<S;++i)f[i]=1,fib[i]=(fib[i-1]+fib[i-2])%mod; 14 for(int i=1;i<S;++i)iv[i]=pw(fib[i],mod-2); 15 for(int i=1;i<S;++i)for(int j=i;j<S;j+=i)if(mu[j/i])f[j]=1ll*f[j]*(mu[j/i]>0?fib[i]:iv[i])%mod; 16 for(int i=2;i<S;++i)f[i]=1ll*f[i-1]*f[i]%mod; 17 int t,n,m,ans=1,N,M,l;cin>>t;while(t--){ 18 cin>>n>>m;if(n>m)n^=m^=n^=m; 19 for(int i=1;N=n/i,M=m/i,i<=n;i=l+1)l=min(n/N,m/M),ans=1ll*ans*pw(1ll*f[l]*pw(f[i-1],mod-2)%mod,1ll*N*M%(mod-1))%mod; 20 cout<<ans<<endl;ans=1; 21 } 22 }
于神之怒加强版:
$Description:$
给下N,M,K.求$sumlimits_{i=1}^{N} sumlimits_{j=1}^{M} gcd(i,j)^K$
$1 le N,M,K le 5000000,1 le T le 2000$
$sumlimits_{i=1}^{N} sumlimits_{j=1}^{M} gcd(i,j)^K$
$=sumlimits_{p} p^K sumlimits_{i=1}^{frac{N}{x}} sumlimits_{j=1}^{frac{M}{x}} [gcd(i,j)]$
$=sumlimits_{p} p^K sumlimits_{d} mu(d) frac{N}{pd} frac{M}{pd}$
$=sumlimits_{x} frac{N}{x} frac{M}{x} sumlimits_{p|x} p^K imes mu(frac{x}{p})$
后者是个积性函数,线筛。然后除法分块。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define mod 1000000007 4 #define S 5000005 5 int pow(int b,int t,int a=1){for(;t;t>>=1,b=1ll*b*b%mod)if(t&1)a=1ll*a*b%mod;return a;} 6 int f[S],p[S],pc,pw[S];char np[S]; 7 int main(){ 8 int t,k,n,m;cin>>t>>k;f[1]=1; 9 for(int i=2;i<S;++i){ 10 if(!np[i])p[++pc]=i,f[i]=(pw[i]=pow(i,k))-1; 11 for(int j=1,x;j<=pc&&(x=i*p[j])<S;++j) 12 if(i%p[j])f[x]=1ll*f[i]*f[p[j]]%mod,np[x]=1; 13 else {f[x]=1ll*f[i]*pw[p[j]]%mod;np[x]=1;break;} 14 } 15 for(int i=2;i<S;++i)f[i]=(f[i]+f[i-1])%mod; 16 while(t--){cin>>n>>m;if(n>m)n^=m^=n^=m;int ans=0; 17 for(int i=1,l,N,M;N=n/i,M=m/i,i<=n;i=l+1)l=min(n/N,m/M),ans=(ans+1ll*N*M%mod*(f[l]-f[i-1]+mod))%mod; 18 cout<<ans<<endl; 19 } 20 }
jzptab:
$Description:$
求$sumlimits_{i=1}^{n} sumlimits_{j=1}^{m} lcm(i,j)$。答案模100000009输出多组询问
$T le 10^4,n,m le 10^7$
$sumlimits_{i=1}^{n} sumlimits_{j=1}^{m} lcm(i,j)$
$=sumlimits_{i=1}^{n} sumlimits_{j=1}^{m} frac{i imes j}{gcd(i,j)}$
$=sumlimits_{p} sumlimits_{i=1}^{frac{n}{p}} sumlimits_{j=1}^{frac{m}{p}} i imes j imes p sumlimits_{d|i and d|j} mu(d)$
$=sumlimits_{p} sumlimits_{d} mu(d) d^2 sumlimits_{i=1}^{frac{n}{pd}} i imes sumlimits_{j=1}^{frac{m}{pd}} j$
后面两个都是等差数列求和,设为$f(x)=sumlimits_{i=1}^{x}i $
$=sumlimits_{x} f(frac{n}{x}) imes f(frac{m}{x}) imes x sumlimits_{d|x} mu(d) imes d$
最后一个和式又可以线筛了。等差数列求和不用说,要把$x$算进后面的那个和式作为一个函数求前缀和比较方便。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define mod 100000009 4 #define S 10000005 5 int p[S],pc,f[S],g[S];char np[S]; 6 int main(){g[1]=1; 7 for(int i=2;i<S;++i){ 8 if(!np[i])p[++pc]=i,g[i]=mod-i+1; 9 for(int j=1,x;j<=pc&&(x=p[j]*i)<S;++j) 10 if(i%p[j])g[x]=1ll*g[i]*g[p[j]]%mod,np[x]=1; 11 else {np[x]=1;g[x]=g[i];break;} 12 }for(int i=1;i<S;++i)g[i]=(1ll*g[i]*i+g[i-1])%mod,f[i]=(f[i-1]+i)%mod; 13 int t,n,m,N,M,l;long long ans=0;cin>>t;while(t--){ 14 cin>>n>>m;if(n>m)n^=m^=n^=m; 15 for(int i=1;N=n/i,M=m/i,i<=n;i=l+1)l=min(n/N,m/M),ans=(ans+1ll*f[N]*f[M]%mod*(g[l]-g[i-1]+mod))%mod; 16 cout<<ans<<endl;ans=0; 17 } 18 }
一个人的数论:
直接附原博客链接。