[关闭]
@xiaoziyao 2021-05-11T15:15:21.000000Z 字数 2210 阅读 781

P7575 「PMOI-3」公约数解题报告

解题报告


P7575 「PMOI-3」公约数解题报告:

更好的阅读体验

题意

给定以及一个长度为的序列(保证互不相同),求:

分析

好久没有反演了,反演一波。

这个式子看上去要硬上莫反,但由于互不相同,因此不好直接反演。

考虑dp,设表示考虑前个数,且第个数强制设为的方案数,那么最后的答案为,且很容易列出转移方程:

这个式子可以上莫反了,化一化就可以得到:

,那么上式可化简:

式子仍然棘手,再次设,那么会有两个式子成立(分别从上面定义式与原式的化简式得来):

,那么我们惊奇地发现的狄利克雷后缀和,的狄利克雷前缀和。

我们知道最开始的,可以直接推出,然后可以通过狄利克雷后缀和推出,然后用的定义推出,再次用狄利克雷前缀和推出,然后推出……

分析一下时间复杂度,由于狄利克雷前/后缀和的复杂度是的,而我们进行狄利克雷前/后缀和的总数组大小为(由于互不相同),所以复杂度应该为。(同阶)

代码

代码具体内容不需要定义这么多数组,两个就够用了。

目前最优解,欢迎吊打。

  1. #include<stdio.h>
  2. const int maxn=1000005,mod=998244353;
  3. int n,m,cnt,ans;
  4. int p[maxn],c[maxn],miu[maxn],x[maxn],f[maxn],g[maxn];
  5. inline int add(int x,int y){
  6. return x+y>=mod? x+y-mod:x+y;
  7. }
  8. void sieve(int n){
  9. c[1]=miu[1]=1;
  10. for(int i=2;i<=n;i++){
  11. if(c[i]==0)
  12. p[++cnt]=i,miu[i]=mod-1;
  13. for(int j=1;j<=cnt;j++){
  14. if(i*p[j]>n)
  15. break;
  16. c[i*p[j]]=1;
  17. if(i%p[j]==0){
  18. miu[i*p[j]]=0;
  19. break;
  20. }
  21. miu[i*p[j]]=miu[i]==0? 0:(mod-miu[i]);
  22. }
  23. }
  24. }
  25. int main(){
  26. scanf("%d%d",&n,&m),sieve(m);
  27. x[0]=1;
  28. for(int i=1;i<n;i++)
  29. scanf("%d",&x[i]);
  30. for(int i=1;i<=m;i++)
  31. f[i]=1;
  32. for(int i=1;i<n;i++){
  33. for(int j=1;j*x[i]<=m;j++)
  34. g[j]=f[j*x[i]];
  35. for(int j=1;j*x[i-1]<=m;j++)
  36. f[j*x[i-1]]=0;
  37. for(int j=1;j<=cnt&&p[j]<=m/x[i];j++)
  38. for(int k=(m/x[i])/p[j];k>=1;k--)
  39. g[k]=add(g[k],g[k*p[j]]);
  40. for(int j=1;j*x[i]<=m;j++)
  41. g[j]=1ll*miu[j]*g[j]%mod;
  42. for(int j=1;j<=cnt&&p[j]<=m/x[i];j++)
  43. for(int k=1;k*p[j]<=m/x[i];k++)
  44. g[k*p[j]]=add(g[k*p[j]],g[k]);
  45. for(int j=1;j*x[i]<=m;j++)
  46. f[j*x[i]]=g[j];
  47. }
  48. for(int i=1;i<=m;i++)
  49. ans=add(ans,f[i]);
  50. printf("%d\n",ans);
  51. return 0;
  52. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注