The 2019 ACM-ICPC China Shannxi Provincial Programming Contest B. Product(杜教筛+约数)

匿名 (未验证) 提交于 2019-12-02 23:38:02
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ftx456789/article/details/91346129

题意

给你n(n109)n(n\leq 10^9)m(m2×109)m(m\leq 2\times10^9)p(p2×109)p(p\leq 2\times10^9 )pp为质数求
i=1nj=1nk=1nmgcd(i,j)[kgcd(i,j)]\prod_{i=1}^n\prod_{j=1}^n\prod_{k=1}^nm^{gcd(i,j)[k|gcd(i,j)]}
ģpp的答案

˼·

我们先只考虑指数部分和欧拉降幂那么有
i=1nj=1nk=1ngcd(i,j)[kgcd(i,j)]modp1\sum_{i=1}^n\sum_{j=1}^n\sum_{k=1}^ngcd(i,j)[k|gcd(i,j)]\ mod\ p-1
可以认为k=1ngcd(i,j)[kgcd(i,j)]\sum_{k=1}^ngcd(i,j)[k|gcd(i,j)]
计算的是gcd(i,j)gcd(i,j)Լgcd(i,j)*gcd(i,j)的约数个数,我们用σ(d)\sigma (d)表示dd的约数个数
那么原式就等于
i=1nj=1ngcd(i,j)σ(gcd(i,j))\sum_{i=1}^n\sum_{j=1}^ngcd(i,j)\sigma(gcd(i,j))
d=gcd(i,j)d=gcd(i,j)
d=1ndσ(d)i=1nj=1n[gcd(i,j)==d]\sum_{d=1}^nd\sigma(d)\sum_{i=1}^n\sum_{j=1}^n[gcd(i,j)==d]

d=1ndσ(d)i=1ndj=1nd[gcd(i,j)==1]\sum_{d=1}^nd\sigma(d)\sum_{i=1}^{\left \lfloor\frac{n}{d}\right \rfloor}\sum_{j=1}^{\left \lfloor\frac{n}{d}\right \rfloor}[gcd(i,j)==1]
考虑后面的部分
i=1ndj=1nd[gcd(i,j)==1]\sum_{i=1}^{\left \lfloor\frac{n}{d}\right \rfloor}\sum_{j=1}^{\left \lfloor\frac{n}{d}\right \rfloor}[gcd(i,j)==1]
可以用莫比乌斯反演做但是这恰好iijj的上限都nn,可以认为是1n1-n中互质的数对数,考虑每个ii的贡献其实就是φ(i)\varphi(i)欧拉函数的值,那么答案就是欧拉函数的前缀和,但是由于iijj的顺序是有影响的然后(1,1)(1,1)只计算一次所以有
i=1ndj=1nd[gcd(i,j)==1]=2Φ(nd)1\sum_{i=1}^{\left \lfloor\frac{n}{d}\right \rfloor}\sum_{j=1}^{\left \lfloor\frac{n}{d}\right \rfloor}[gcd(i,j)==1]=2*\Phi(\left \lfloor\frac{n}{d}\right \rfloor)-1
其中φ\varphi为欧拉函数的和函数
那么原式就转化为
=d=1ndσ(d)(2Φ(nd)1)=\sum_{d=1}^nd\sigma(d)(2*\Phi(\left \lfloor\frac{n}{d}\right \rfloor)-1)
然后我们再考虑一下
i=1niσ(i)\sum_{i=1}^ni\sigma(i)
这个求和
可以把约数函数展开来
=i=1nidi1=\sum_{i=1}^ni\sum_{d|i}1
那么表示的就是对于每一个ii枚举他的因子有哪些,已知在1n1-n中枚举ii的因子和枚举ii的倍数是等价的那么就有
=d=1ndii=\sum_{d=1}^n\sum_{d|i}i
iiΪdd的倍数,我们再把后面的展开来
=d=1n(d+2d+3d++ndd)=\sum_{d=1}^n(d+2d+3d+\cdots+\left \lfloor\frac{n}{d}\right \rfloor d)
后面就是一个等差数列
=d=1ndnd(nd+1)2=\sum_{d=1}^nd\frac{\left \lfloor\frac{n}{d}\right \rfloor(\left \lfloor\frac{n}{d}\right \rfloor+1)}{2}
Id(n)=i=1niσ(i)Id(n)=\sum_{i=1}^ni\sigma(i)
原式想用分块优化的话那么有last=nnilast=\frac{n}{\frac{n}{i}}

=i=last+1n(Id(last)Id(i1))(2Φ(ni)1)=\sum_{i=last+1}^n(Id(last)-Id(i-1))(2*\Phi(\left \lfloor\frac{n}{i}\right \rfloor)-1)
Id(n)Id(n)可以用线性筛处理一部分(先求每个数的约数个数再求前缀和的时候乘以i),大于的部分就用上面求的分块来处理了,欧拉函数求和用杜教筛即可
求完这个后最后的答案求一个快速幂就可以了,尽量都用int,不然会超时

#include<bits/stdc++.h> #include <unordered_map> using namespace std; typedef long long ll; const int N=10000005; int mod; unordered_map<int,int> P; unordered_map<int,int> D; bool isP[N]; int prime[N]; int cnt; int phi[N]; int d[N]; int num[N]; long long inv=500000004; void init() {     phi[1]=d[1]=1;     for(int i=2; i<N; i++)     {         if(!isP[i])         {             prime[cnt++]=i;             phi[i]=i-1;             d[i]=2;             num[i]=1;         }         for(int j=0; j<cnt&&(ll)i*prime[j]<N; j++)         {             isP[i*prime[j]]=true;             if(i%prime[j])             {                 phi[i*prime[j]]=phi[i]*(prime[j]-1);                 d[i*prime[j]]=d[i]*d[prime[j]];                 num[i*prime[j]]=1;             }             else             {                 phi[i*prime[j]]=phi[i]*prime[j];                 num[i*prime[j]]=num[i]+1;                 d[i*prime[j]]=d[i]/(num[i]+1)*(num[i*prime[j]]+1);                 break;             }         }     }     for(int i=1; i<N; i++)     {         phi[i]=(phi[i]+phi[i-1])%mod;         d[i]=((ll)i*d[i]+d[i-1])%mod;     } } int Sum(int x) {     if(x<N)return phi[x];     if(P[x])return P[x];     int ans=(x+1ll)*x/2%mod;     for(int i=2,last; i<=x; i=last+1)     {         last=x/(x/i);         ans=(ans-(last-i+1ll)*Sum(x/i)%mod+mod)%mod;     }     ans=((ll)ans+mod)%mod;     P[x]=ans;     return ans; } int Id(int n) {     if(n<N) return d[n];     else if(D[n]) return D[n];     int ans=0;     for(int i=1,last; i<=n; i=last+1)     {         last=n/(n/i);         ans=(ans+(last+i)*(last-i+1ll)/2%mod*((n/i*(n/i+1ll)/2)%mod)%mod)%mod;     }     D[n]=ans;     return ans; } long long quickmod(long long a,long long b,long long p) {     long long ans=1;     while(b)     {         if(b%2==1)             ans=ans*a%p;         a=a*a%p;         b=b/2;     }     return ans; } int main() {     int n,m,p;     scanf("%d%d%d",&n,&m,&p);     mod=p-1;     init();     int ans=0;     for(int i=1,last; i<=n; i=last+1)     {         last=n/(n/i);         ans=(ans+(Id(last)-Id(i-1)+mod)%mod*((2ll*Sum(n/i)%mod-1ll+mod)%mod)%mod+mod)%mod;     }     printf("%lld\n",quickmod(m,ans,p));     return 0; } /* 1000000000 999999997 98765431 */ 
文章来源: https://blog.csdn.net/ftx456789/article/details/91346129
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!