[关闭]
@xiaoziyao 2020-11-24T20:20:22.000000Z 字数 6457 阅读 1041

CF809E Surprise me!解题报告

解题报告


CF809E Surprise me!解题报告:

题意

分析

虽然交不了题解了,但是还是记录一下这道大毒瘤题。

由于不太好求,我们考虑是一个排列,因此我们先设,即为每个值的位置,然后带进去:

我们首先根据欧拉函数的一个公式变一下式子(证明在题解末尾),并对于这个式子套路地反演一下(先忽略掉前面的):

套路地设,那么有:

对于前面的东西,我们设,这个东西很显然可以先线性筛出来,然后暴力枚举倍数做到求出。

然后,我们把带回原来的式子:

对于后面的东西,我们发现对于每个,用到的点只有个,因此,我们用到的总点数为个。

如果设为第次的总点数,我们有,这个数据范围启发我们来建立一颗虚树。

对于每个,我们暴力枚举它的所有倍数的点,建立一颗虚树,然后在上面跑树形

具体地,我们设子树中的全体贡献,即,并设两个辅助数组

考虑合并两颗子树的儿子),那么答案为子树的贡献加子树的贡献加两两之间的贡献,即

拆开:

的转移和的转移简单一些:


最后的答案即为

由于建立虚树是的,所以总复杂度为

代码

  1. #include<stdio.h>
  2. #include<vector>
  3. #include<algorithm>
  4. using namespace std;
  5. const int maxn=200005,maxm=maxn<<1,mod=1000000007,maxk=25;
  6. int n,e,cnt,ans,top,stp;
  7. int a[maxn],start[maxn],to[maxm],then[maxm],f[maxn],P[maxn],p[maxn],pos[maxn],dfn[maxn],dep[maxn],fore[maxn][maxk],dp1[maxn],dp2[maxn],dp3[maxn],ok[maxn],stk[maxn],miu[maxn],phi[maxn],nphi[maxn];
  8. vector<int>vp[maxn],g[maxn];
  9. inline void add(int x,int y){
  10. then[++e]=start[x],start[x]=e,to[e]=y;
  11. }
  12. inline void newadd(int x,int y){
  13. g[x].push_back(y);
  14. }
  15. inline int cmp(int a,int b){
  16. return dfn[a]<dfn[b];
  17. }
  18. void dfs(int x,int last){
  19. dfn[x]=++cnt,dep[x]=dep[last]+1,fore[x][0]=last;
  20. for(int i=1;i<=20;i++)
  21. fore[x][i]=fore[fore[x][i-1]][i-1];
  22. for(int i=start[x];i;i=then[i]){
  23. int y=to[i];
  24. if(y==last)
  25. continue;
  26. dfs(y,x);
  27. }
  28. }
  29. int lca(int a,int b){
  30. if(dep[a]<dep[b])
  31. swap(a,b);
  32. for(int i=20;i>=0;i--)
  33. if(dep[fore[a][i]]>=dep[b])
  34. a=fore[a][i];
  35. if(a==b)
  36. return a;
  37. for(int i=20;i>=0;i--)
  38. if(fore[a][i]!=fore[b][i])
  39. a=fore[a][i],b=fore[b][i];
  40. return fore[a][0];
  41. }
  42. void DP(int x,int k){
  43. dp1[x]=dp2[x]=0,dp3[x]=a[x]%k==0? phi[a[x]]:0;
  44. for(int i=0;i<g[x].size();i++){
  45. int y=g[x][i],z=(dep[y]-dep[x]+mod)%mod;
  46. DP(y,k);
  47. dp1[x]+=((dp1[y]+1ll*(dp2[x]+1ll*dp3[x]*z%mod)%mod*dp3[y])%mod+1ll*dp2[y]*dp3[x]%mod)%mod;
  48. dp2[x]+=(dp2[y]+1ll*dp3[y]*z%mod)%mod,dp3[x]+=dp3[y];
  49. dp1[x]%=mod,dp2[x]%=mod,dp3[x]%=mod;
  50. }
  51. }
  52. int calc(int k){
  53. top=0,stp++;
  54. for(int i=0;i<vp[k].size();i++)
  55. ok[vp[k][i]]=stp;
  56. sort(vp[k].begin(),vp[k].end(),cmp);
  57. g[1].clear(),stk[++top]=1;
  58. for(int i=0;i<vp[k].size();i++){
  59. if(vp[k][i]==1)
  60. continue;
  61. int l=lca(vp[k][i],stk[top]);
  62. if(l!=stk[top]){
  63. while(top>0&&dfn[l]<dfn[stk[top-1]])
  64. newadd(stk[top-1],stk[top]),top--;
  65. if(dfn[l]>dfn[stk[top-1]])
  66. g[l].clear(),newadd(l,stk[top]),top--,stk[++top]=l;
  67. else newadd(stk[top-1],stk[top]),top--;
  68. }
  69. g[vp[k][i]].clear(),stk[++top]=vp[k][i];
  70. }
  71. for(int j=1;j<top;j++)
  72. newadd(stk[j],stk[j+1]);
  73. DP(1,k);
  74. return dp1[1];
  75. }
  76. int ksm(int a,int b){
  77. int res=1;
  78. while(b){
  79. if(b&1)
  80. res=1ll*res*a%mod;
  81. a=1ll*a*a%mod,b>>=1;
  82. }
  83. return res;
  84. }
  85. void sieve(){
  86. p[1]=phi[1]=miu[1]=nphi[1]=1;
  87. for(int i=2;i<=n;i++){
  88. if(p[i]==0)
  89. P[++cnt]=i,phi[i]=i-1,miu[i]=-1;
  90. for(int j=1;j<=cnt;j++){
  91. if(i*P[j]>n)
  92. break;
  93. p[i*P[j]]=1;
  94. if(i%P[j]==0){
  95. phi[i*P[j]]=phi[i]*P[j];
  96. miu[i*P[j]]=0;
  97. break;
  98. }
  99. phi[i*P[j]]=phi[i]*(P[j]-1);
  100. miu[i*P[j]]=-miu[i];
  101. }
  102. nphi[i]=ksm(phi[i],mod-2);
  103. }
  104. }
  105. int main(){
  106. scanf("%d",&n);
  107. sieve();
  108. for(int i=1;i<=n;i++){
  109. scanf("%d",&a[i]);
  110. pos[a[i]]=i;
  111. }
  112. for(int i=1;i<n;i++){
  113. int x,y;
  114. scanf("%d%d",&x,&y);
  115. add(x,y),add(y,x);
  116. }
  117. dfs(1,0);
  118. for(int i=1;i<=n;i++)
  119. for(int j=i;j<=n;j+=i){
  120. f[j]=(f[j]+1ll*i*miu[j/i]%mod*nphi[i]%mod)%mod;
  121. f[j]=(f[j]+mod)%mod;
  122. }
  123. for(int i=1;i<=n;i++)
  124. for(int j=i;j<=n;j+=i)
  125. vp[i].push_back(pos[j]);
  126. for(int i=1;i<=n;i++)
  127. ans+=2ll*f[i]*calc(i)%mod,ans%=mod;
  128. printf("%d\n",(int)(1ll*ans*ksm(n,mod-2)%mod*ksm(n-1,mod-2)%mod));
  129. return 0;
  130. }

关于欧拉函数公式的证明

证明:


由欧拉函数的公式有:

我们根据容斥的思想可以得到:

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注