[关闭]
@xiaoziyao 2020-08-28T12:13:05.000000Z 字数 4188 阅读 800

P6788 「EZEC-3」四月樱花解题报告

解题报告


P6788 「EZEC-3」四月樱花解题报告:

更好的阅读体验

这是一道比较套路的推柿子题,代码短,可惜我不会做

题意


对一质数取模,其中的约数个数。

数据范围:,

分析

考场上看到就想到去了,结果死活想不出来用来替代,最后写了个的暴力丢人/kk。

可能是求和做多了,一看到求积就蒙了

首先我们有:

所以原式可以转换一下:

然后比较套路地枚举,这样式子可以转化为:

套路枚举倍数,然后把求积转化为指数上的求和:

注意一下,这里有一个细节,第二个式子的第三个中:,之所以上面是而不是,是因为这里是枚举的倍数,此时代表的数应该为

展开,就会有:

因为,所以上面的指数可以看做,其中,这样我们的指数就可以整除分块做到根号的复杂度了。

求积其实也是一样的,因为它们的指数满足整除分块的性质,所以可以对进行整除分块。

但是还有一个小问题,如何求。我们只需要展开这个求积式,就有


我们只需要抵消一下,答案就是了。

代码

  1. #include<stdio.h>
  2. #define int long long
  3. int i,j,k,m,n,mod,l,r,ans;
  4. int ksm(int a,int b){
  5. b%=(mod-1);
  6. int res=1;
  7. while(b){
  8. if(b&1)
  9. res=res*a%mod;
  10. a=a*a%mod,b>>=1;
  11. }
  12. return res;
  13. }
  14. int calc(int n){
  15. int l=1,r,res=0;
  16. while(l<=n){
  17. r=n/(n/l);
  18. res+=(r-l+1)*(n/l);
  19. l=r+1;
  20. }
  21. return res;
  22. }
  23. signed main(){
  24. scanf("%lld%lld",&n,&mod);
  25. l=1,r=0,ans=1;
  26. while(l<=n){
  27. r=n/(n/l);
  28. ans=(ans*ksm(l*ksm(r+1,mod-2)%mod,calc(n/l))%mod)%mod;
  29. l=r+1;
  30. }
  31. printf("%lld\n",ans*ans%mod);
  32. return 0;
  33. }

更新

然而现在出题人卡掉了上述的算法,现在对上面进行加速。

把上面的式子搬下来:

上面的指数我们还可以化为:,然后我们可以用一个神奇的科技来处理的前缀和:杜教套杜教!

因为,所以有

然后掏出杜教筛的式子·

带入进去:

我们需要对后面的式子进行整除分块,所以我们需要筛出的前缀和,这里需要另一个杜教筛(

代码

  1. #include<stdio.h>
  2. #include<map>
  3. #define int long long
  4. using namespace std;
  5. const int maxn=5000005;
  6. int i,j,k,m,n,mod,ans,cnt;
  7. int a[maxn],p[maxn],d[maxn],r[maxn],mu[maxn],sumd[maxn],summu[maxn];
  8. map<int,int>ansd,ansmu;
  9. int ksm(int a,int b){//快速幂
  10. b%=(mod-1);//用费马小定理处理一下
  11. int res=1;
  12. while(b){
  13. if(b&1)
  14. res=res*a%mod;
  15. a=a*a%mod,b>>=1;
  16. }
  17. return res;
  18. }
  19. int getmu(int n){//杜教筛筛出莫比乌斯函数
  20. if(n<maxn)
  21. return summu[n];
  22. if(ansmu.count(n))
  23. return ansmu[n];
  24. int l=2,r,res=1;
  25. while(l<=n){
  26. r=n/(n/l);
  27. res-=(r-l+1)*getmu(n/l);
  28. l=r+1;
  29. }
  30. return ansmu[n]=res;
  31. }
  32. int getd(int n){//杜教筛筛出约数函数
  33. if(n<maxn)
  34. return sumd[n];
  35. if(ansd.count(n))
  36. return ansd[n];
  37. int l=2,r,res=n;
  38. while(l<=n){
  39. r=n/(n/l);
  40. res-=(getmu(r)-getmu(l-1))*getd(n/l);
  41. l=r+1;
  42. }
  43. return ansd[n]=res;
  44. }
  45. void init(){//线性筛筛出2/3的前缀和
  46. p[1]=1,d[1]=1,r[1]=1,mu[1]=1;
  47. for(int i=2;i<maxn;i++){
  48. if(p[i]==0)
  49. a[++cnt]=i,d[i]=2,r[i]=1,mu[i]=-1;
  50. for(int j=1;j<=cnt;j++){
  51. if(i*a[j]>=maxn)
  52. break;
  53. p[i*a[j]]=1;
  54. if(i%a[j]==0){
  55. r[i*a[j]]=r[i]+1;
  56. d[i*a[j]]=d[i]/r[i*a[j]]*(r[i*a[j]]+1);
  57. mu[i*a[j]]=0;
  58. break;
  59. }
  60. r[i*a[j]]=1;
  61. d[i*a[j]]=d[i]*2;
  62. mu[i*a[j]]=-mu[i];
  63. }
  64. }
  65. for(int i=1;i<maxn;i++)
  66. sumd[i]=sumd[i-1]+d[i],summu[i]=summu[i-1]+mu[i];
  67. }
  68. int getans(int n){//对答案进行整除分块
  69. int l=1,r=0,res=1;
  70. while(l<=n){
  71. r=n/(n/l);
  72. res=(res*ksm(l*ksm(r+1,mod-2)%mod,getd(n/l))%mod)%mod;
  73. l=r+1;
  74. }
  75. return res;
  76. }
  77. signed main(){
  78. scanf("%lld%lld",&n,&mod);
  79. init();
  80. ans=getans(n);
  81. printf("%lld\n",ans*ans%mod);
  82. return 0;
  83. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注