函树(hs) - 莫比乌斯反演 - 虚树

题目大意:给一颗树,求 ∑ x , y φ ( x × y ) dist ( x , y ) \sum_{x,y}\varphi(x\times y)\text{dist}(x,y) x,yφ(x×y)dist(x,y) n ≤ 1 0 5 n\le10^5 n105
题解:
∑ x , y φ ( x × y ) dist ( x , y ) = ∑ x , y φ ( x ) φ ( y ) gcd ⁡ ( x , y ) φ ( gcd ⁡ ( x , y ) ) dist ( x , y ) = ∑ d = 1 n d φ ( d ) ∑ d ∣ x , d ∣ y φ ( x ) φ ( y ) dist ( x , y ) [ gcd ⁡ ( x , y ) = d ] = ∑ d = 1 n d φ ( d ) ∑ d ∣ x , d ∣ y φ ( x ) φ ( y ) dist ( x , y ) ∑ e ∣ x d , e ∣ y d μ ( e ) = ∑ T = 1 n F ( T ) ∑ d ∣ T d φ ( d ) μ ( T d ) \sum_{x,y}\varphi(x\times y)\text{dist}(x,y)\\ =\sum_{x,y}\frac{\varphi(x)\varphi(y)\gcd(x,y)}{\varphi(\gcd(x,y))}\text{dist}(x,y)\\ =\sum_{d=1}^n\frac{d}{\varphi(d)}\sum_{d|x,d|y}\varphi(x)\varphi(y)\text{dist}(x,y)\left[\gcd(x,y)=d\right]\\ =\sum_{d=1}^n\frac{d}{\varphi(d)}\sum_{d|x,d|y}\varphi(x)\varphi(y)\text{dist}(x,y)\sum_{e|\frac xd,e|\frac yd}\mu(e)\\ =\sum_{T=1}^n\text F(T)\sum_{d|T}\frac d{\varphi(d)}\mu\left(\frac Td\right) x,yφ(x×y)dist(x,y)=x,yφ(gcd(x,y))φ(x)φ(y)gcd(x,y)dist(x,y)=d=1nφ(d)ddx,dyφ(x)φ(y)dist(x,y)[gcd(x,y)=d]=d=1nφ(d)ddx,dyφ(x)φ(y)dist(x,y)edx,edyμ(e)=T=1nF(T)dTφ(d)dμ(dT)
其中 F ( T ) \text F(T) F(T)表示把所有两端点编号是 T T T的倍数的路径拿出来,求端点的 φ \varphi φ的乘积再求和。这个直接虚树即可。实现优秀可以 O ( n lg ⁡ n ) O\left(n\lg n\right) O(nlgn)

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,v) rep(i,0,(int)v.size()-1)
#define lint long long
#define mod 998244353
#define ull unsigned lint
#define db long double
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
typedef set<int>::iterator sit;
namespace INPUT_SPACE{
    const int BS=(1<<24)+5;char Buffer[BS],*HD,*TL;inline int gc() { if(HD==TL) TL=(HD=Buffer)+fread(Buffer,1,BS,stdin);return (HD==TL)?EOF:*HD++; }
    inline int inn() { int x,ch;while((ch=gc())<'0'||ch>'9');x=ch^'0';while((ch=gc())>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^'0');return x; }
}using INPUT_SPACE::inn;
const int N=100010;
struct edges{
    int to,pre;
}e[N<<1],dvs[1166750+100];int h[N],etop,dh[N],dtop,*lst[N],lstc[N],d[N],tms[N];
inline int add_edge(int u,int v) { return e[++etop].to=v,e[etop].pre=h[u],h[u]=etop; }
inline int add_dvs(int u,int v) { return dvs[++dtop].to=v,dvs[dtop].pre=dh[u],dh[u]=dtop; }
inline int fast_pow(int x,int k,int ans=1) { for(;k;k>>=1,x=(lint)x*x%mod) if(k&1) ans=(lint)ans*x%mod;return ans; }
namespace PRELUDE_space{
    int mu[N],phi[N],val[N],p[N],np[N],fac[N],facinv[N],inv[N];
    inline int prelude(int n)
    {
        phi[1]=mu[1]=1;
        for(int i=2,c=0;i<=n;i++)
        {
            if(!np[i]) p[++c]=i,phi[i]=i-1,mu[i]=-1;
            rep(j,1,c&&p[j]<=n/i)
            {
                int x=i*p[j];np[x]=1;
                if(i%p[j]==0) { phi[x]=phi[i]*p[j],mu[x]=0;break; }
                else phi[x]=phi[i]*(p[j]-1),mu[x]=-mu[i];
            }
        }
        rep(i,fac[0]=1,n) fac[i]=(lint)fac[i-1]*i%mod;
        facinv[n]=fast_pow(fac[n],mod-2);
        for(int i=n-1;i>=0;i--) facinv[i]=(i+1ll)*facinv[i+1]%mod;
        rep(i,1,n) inv[i]=(lint)facinv[i]*fac[i-1]%mod;
        rep(d,1,n) rep(e,1,n/d) if(mu[e])
        {
            int t=d*e;
            if(mu[e]>0) val[t]=(val[t]+(lint)d*inv[phi[d]])%mod;
            else val[t]=(val[t]-(lint)d*inv[phi[d]])%mod,(val[t]<0?val[t]+=mod:0);
        }
        return 0;
    }
}using PRELUDE_space::val;using PRELUDE_space::phi;
namespace LCA_space{
    const int LOG=20;
    int in[N],fir[N],f[N<<1][LOG],Log[N<<1],dfc,cnt;
    int dfs(int x,int fa=0)
    {
        tms[in[x]=++dfc]=x,d[x]=d[fa]+1,f[fir[x]=++cnt][0]=x;
        for(int i=h[x],y;i;i=e[i].pre)
            if((y=e[i].to)^fa) dfs(y,x),f[++cnt][0]=x;
        return 0;
    }
    inline int Mymin(int x,int y) { return d[x]<d[y]?x:y; }
    inline int build_ST()
    {
        rep(i,2,cnt) Log[i]=Log[i>>1]+1;
        for(int j=1;(1<<j)<=cnt;j++) rep(i,1,cnt-(1<<j)+1)
            f[i][j]=Mymin(f[i][j-1],f[i+(1<<(j-1))][j-1]);
        return 0;
    }
    inline int LCA(int x,int y)
    {
        x=fir[x],y=fir[y];if(x>y) swap(x,y);int k=Log[y-x+1];
        return Mymin(f[x][k],f[y-(1<<k)+1][k]);
    }
}using LCA_space::dfs;using LCA_space::LCA;
namespace SOLVE_space{
    int p[N],dp[N],imp[N],ans,s[N];vector<int> g[N];
    inline int getans(int x)
    {
        p[x]=dp[x]=0;if(imp[x]) p[x]=phi[x];
        Rep(i,g[x])
        {
            int y=g[x][i];getans(y);
            dp[y]=(dp[y]+(lint)p[y]*(d[y]-d[x]))%mod,
            ans=(ans+(lint)p[x]*dp[y]+(lint)p[y]*dp[x])%mod,
            dp[x]+=dp[y],(dp[x]>=mod?dp[x]-=mod:0),
            p[x]+=p[y],(p[x]>=mod?p[x]-=mod:0);
        }
        return 0;
    }
    inline int solve(int *lst,int n,int qwq)
    {
        int t;s[t=1]=lst[1];
        rep(i,1,n) g[lst[i]].clear();
        rep(i,2,n)
        {
            int x=lst[i],y=s[t],z=s[t-1],c;
            while(d[c=LCA(x,y)]<=d[z]) g[z].pb(y),y=s[--t],z=s[t-1];
            if(c^y) g[c].clear(),g[c].pb(y),s[t]=c;s[++t]=x;
        }
        while(t>1) g[s[t-1]].pb(s[t]),t--;
        rep(i,1,n) imp[lst[i]]=1;imp[1]=qwq;
        ans=0,getans(lst[1]);
        rep(i,1,n) imp[lst[i]]=0;
        return ans;
    }
}using SOLVE_space::solve;
int main()
{
    int n=inn(),x,y,ans=0;PRELUDE_space::prelude(n);
    rep(i,1,n-1) x=inn(),y=inn(),add_edge(x,y),add_edge(y,x);
    dfs(1),LCA_space::build_ST();
    rep(i,1,n) rep(j,1,n/i) add_dvs(i*j,i);
    rep(i,1,n) lst[i]=new int[n/i+5];
    rep(i,2,n) lst[i][lstc[i]=1]=1;
    rep(t,1,n)
    {
        int x=tms[t];
        for(int i=dh[x],y;i;i=dvs[i].pre)
            y=dvs[i].to,lst[y][++lstc[y]]=x;
    }
    rep(i,1,n) ans=(ans+2ll*solve(lst[i],lstc[i],i==1)*val[i])%mod;
    return !printf("%d\n",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值