@xiaoziyao
        
        2020-08-28T12:13:05.000000Z
        字数 4188
        阅读 1381
    解题报告
P6788 「EZEC-3」四月樱花解题报告:
这是一道比较套路的推柿子题,代码短,可惜我不会做
求 
对一质数取模,其中为的约数个数。
数据范围:,
考场上看到就想到去了,结果死活想不出来用来替代,最后写了个的暴力丢人/kk。
可能是求和做多了,一看到求积就蒙了
首先我们有: 
所以原式可以转换一下: 
然后比较套路地枚举,这样式子可以转化为: 
套路枚举倍数,然后把求积转化为指数上的求和: 
注意一下,这里有一个细节,第二个式子的第三个中:,之所以上面是而不是,是因为这里是枚举的倍数,此时代表的数应该为。
展开,就会有: 
因为,所以上面的指数可以看做,其中,这样我们的指数就可以整除分块做到根号的复杂度了。
对求积其实也是一样的,因为它们的指数满足整除分块的性质,所以可以对进行整除分块。
但是还有一个小问题,如何求。我们只需要展开这个求积式,就有 
#include<stdio.h>#define int long longint i,j,k,m,n,mod,l,r,ans;int ksm(int a,int b){b%=(mod-1);int res=1;while(b){if(b&1)res=res*a%mod;a=a*a%mod,b>>=1;}return res;}int calc(int n){int l=1,r,res=0;while(l<=n){r=n/(n/l);res+=(r-l+1)*(n/l);l=r+1;}return res;}signed main(){scanf("%lld%lld",&n,&mod);l=1,r=0,ans=1;while(l<=n){r=n/(n/l);ans=(ans*ksm(l*ksm(r+1,mod-2)%mod,calc(n/l))%mod)%mod;l=r+1;}printf("%lld\n",ans*ans%mod);return 0;}
然而现在出题人卡掉了上述的算法,现在对上面进行加速。
把上面的式子搬下来: 
上面的指数我们还可以化为:,然后我们可以用一个神奇的科技来处理的前缀和:杜教套杜教!
因为,所以有
然后掏出杜教筛的式子· 
带入进去: 
我们需要对后面的式子进行整除分块,所以我们需要筛出的前缀和,这里需要另一个杜教筛(
#include<stdio.h>#include<map>#define int long longusing namespace std;const int maxn=5000005;int i,j,k,m,n,mod,ans,cnt;int a[maxn],p[maxn],d[maxn],r[maxn],mu[maxn],sumd[maxn],summu[maxn];map<int,int>ansd,ansmu;int ksm(int a,int b){//快速幂b%=(mod-1);//用费马小定理处理一下int res=1;while(b){if(b&1)res=res*a%mod;a=a*a%mod,b>>=1;}return res;}int getmu(int n){//杜教筛筛出莫比乌斯函数if(n<maxn)return summu[n];if(ansmu.count(n))return ansmu[n];int l=2,r,res=1;while(l<=n){r=n/(n/l);res-=(r-l+1)*getmu(n/l);l=r+1;}return ansmu[n]=res;}int getd(int n){//杜教筛筛出约数函数if(n<maxn)return sumd[n];if(ansd.count(n))return ansd[n];int l=2,r,res=n;while(l<=n){r=n/(n/l);res-=(getmu(r)-getmu(l-1))*getd(n/l);l=r+1;}return ansd[n]=res;}void init(){//线性筛筛出2/3的前缀和p[1]=1,d[1]=1,r[1]=1,mu[1]=1;for(int i=2;i<maxn;i++){if(p[i]==0)a[++cnt]=i,d[i]=2,r[i]=1,mu[i]=-1;for(int j=1;j<=cnt;j++){if(i*a[j]>=maxn)break;p[i*a[j]]=1;if(i%a[j]==0){r[i*a[j]]=r[i]+1;d[i*a[j]]=d[i]/r[i*a[j]]*(r[i*a[j]]+1);mu[i*a[j]]=0;break;}r[i*a[j]]=1;d[i*a[j]]=d[i]*2;mu[i*a[j]]=-mu[i];}}for(int i=1;i<maxn;i++)sumd[i]=sumd[i-1]+d[i],summu[i]=summu[i-1]+mu[i];}int getans(int n){//对答案进行整除分块int l=1,r=0,res=1;while(l<=n){r=n/(n/l);res=(res*ksm(l*ksm(r+1,mod-2)%mod,getd(n/l))%mod)%mod;l=r+1;}return res;}signed main(){scanf("%lld%lld",&n,&mod);init();ans=getans(n);printf("%lld\n",ans*ans%mod);return 0;}