【2019北京集训测试赛(十三)】函树 虚树

题目大意:给你一颗n个节点的树,定义d(x,y)=x到点y最短路上经过的边数。

ni=1nj=1φ(i×j)×d(i,j)

答案对998244353$取模。

 

我们对这个式子做一些细微的处理,设最终的答案为ans

ans=ni=1nj=1φ(i×j)×d(i,j)

=ni=1nj=1φ(i)φ(j)gcd(i,j)φ(gcd(i,j))×d(i,j)

我们设F(d)=ni=1nj=1,d|gcd(i,j)φ(i)φ(j)×d(i,j)

那么,ans=nd=1dφ(d)p|dF(p)×G(dp)

对于G(x),设x=ki=1pipi是质数,G(x)=(1)k

 

我们考虑如何求F(d)

显然,我们只需要把所有点权能被d整除的点找出来,建一棵虚树,统计每条虚树边两端的$sum \varphi(i)$,把它们乘起来,再乘上虚树边边长即可。

由于点权等于编号,所以n棵虚树的总点数是O(nln n)级别的,单次构建虚树的复杂度是O(sizelog size)的,所以并不会T掉。

然后就没有了,总复杂度是O(nlog2 n)的。

复制代码
  1 #include<bits/stdc++.h>
  2 #define M 100005
  3 #define MOD 998244353
  4 #define L long long
  5 using namespace std;
  6 
  7 int pri[M]={0},b[M]={0},phi[M]={0},zf[M]={0},Use=0;
  8 void init(){
  9     phi[1]=1; zf[1]=1;
 10     for(int i=2;i<M;i++){
 11         if(!b[i]) pri[++Use]=i,phi[i]=i-1,zf[i]=-1;
 12         for(int j=1;j<=Use&&i*pri[j]<M;j++){
 13             b[i*pri[j]]=1; zf[i*pri[j]]=-zf[i];
 14             if(i%pri[j]==0) {phi[i*pri[j]]=phi[i]*pri[j]; break;}
 15             phi[i*pri[j]]=phi[i]*(pri[j]-1);
 16         }
 17     }
 18 }
 19 
 20 L pow_mod(L x,L k){L ans=1; for(;k;k>>=1,x=x*x%MOD) if(k&1) ans=ans*x%MOD; return ans;}
 21 vector<int> G[M];
 22 
 23 struct edge{int u,v,next;}e[M*2]={0}; int head[M]={0},use=0;
 24 void add(int x,int y,int z){use++;e[use].u=y;e[use].v=z;e[use].next=head[x];head[x]=use;}
 25 int n,a[M]={0};
 26 
 27 int dep[M]={0},dfn[M]={0},low[M]={0},f[M][20]={0},t=0;
 28 void dfs(int x,int fa){
 29     dep[x]=dep[fa]+1; dfn[x]=++t; f[x][0]=fa;
 30     for(int i=1;i<20;i++) f[x][i]=f[f[x][i-1]][i-1];
 31     for(int i=0;i<G[x].size();i++) if(G[x][i]!=fa) dfs(G[x][i],x);
 32     low[x]=t;
 33 }
 34 int getlca(int x,int y){
 35     if(dep[x]<dep[y]) swap(x,y); int cha=dep[x]-dep[y];
 36     for(int i=19;~i;i--) if((1<<i)&cha) x=f[x][i];
 37     if(x==y) return x;
 38     for(int i=19;~i;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
 39     return f[x][0];
 40 }
 41 
 42 vector<int> D[M]; L F[M]={0};
 43 
 44 bool cmp(int x,int y){return dfn[x]<dfn[y];}
 45 int point[M]={0},stk[M]={0},is[M]={0},pcnt=0,cnt=0,nowt=0;
 46 void build(){
 47     pcnt=cnt; int siz=0; nowt=0;
 48     sort(point+1,point+cnt+1,cmp);
 49     for(int i=1;i<=cnt;i++){
 50         int last=0;
 51         while(siz&&getlca(stk[siz],point[i])!=stk[siz]) last=stk[siz],stk[siz--]=0;
 52         if(last){
 53             int lca=getlca(last,point[i]);
 54             if(lca!=stk[siz]){
 55                 stk[++siz]=lca;
 56                 point[++pcnt]=lca;
 57                 is[lca]=0;
 58             }
 59         }
 60         stk[++siz]=point[i]; is[point[i]]=1;
 61     }
 62     sort(point+1,point+pcnt+1,cmp);
 63     while(siz) stk[siz--]=0;
 64 }
 65 L sumphi[M]={0},sum=0;
 66 int dfs(int x){
 67     if(is[x]) sumphi[x]=phi[a[x]]; else sumphi[x]=0; 
 68     int v; nowt++;
 69     while(dfn[v=point[nowt]]<=low[x]&&nowt<=pcnt){
 70         dfs(v);
 71         sumphi[x]=(sumphi[x]+sumphi[v])%MOD;
 72     }
 73 }
 74 void getans(int x,L fsum){
 75     int v; nowt++; 
 76     while(dfn[v=point[nowt]]<=low[x]&&nowt<=pcnt){
 77         sum=(sum+1LL*(dep[v]-dep[x])*sumphi[v]%MOD*(fsum+sumphi[x]-sumphi[v]+MOD))%MOD;
 78         getans(v,(fsum+sumphi[x]-sumphi[v])%MOD);
 79     }
 80 }
 81 void solve(int x){
 82     while(pcnt)point[pcnt--]=0; cnt=sum=0;
 83     for(int i=0;i<D[x].size();i++){
 84         point[++cnt]=D[x][i];
 85     }
 86     build();
 87     nowt=1; dfs(point[1]);
 88     nowt=1; getans(point[1],0);
 89     F[x]=sum;
 90 }
 91 
 92 int main(){
 93 //    freopen("in.txt","r",stdin);
 94 //    freopen("out.txt","w",stdout);
 95     init();
 96     scanf("%d",&n);
 97     for(int i=1;i<=n;i++){
 98         a[i]=i; //scanf("%d",a+i);
 99         for(int j=1;j*j<=a[i];j++) if(a[i]%j==0){
100             D[j].push_back(i);
101             if(j*j!=a[i]) D[a[i]/j].push_back(i);
102         }
103     }
104     for(int i=1;i<n;i++){
105         int x,y; scanf("%d%d",&x,&y);
106         G[x].push_back(y); G[y].push_back(x);
107     }
108     dfs(1,0);
109     for(int i=1;i<=n;i++) solve(i);
110     for(int i=n;i;i--){
111         for(int j=i*2;j<=n;j+=i)
112         F[i]=(F[i]-F[j]+MOD)%MOD;
113     }
114     L ans=0;
115     for(int d=1;d<=n;d++)
116     ans=(ans+F[d]*d%MOD*pow_mod(phi[d],MOD-2))%MOD;
117     cout<<ans*2%MOD<<endl;
118     //cout<<ans*2*pow_mod(1LL*n*(n-1)%MOD,MOD-2)%MOD<<endl;
119 }
复制代码

 

posted @   AlphaInf  阅读(277)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
点击右上角即可分享
微信分享提示