计蒜客 青云的机房组网方案(莫比乌斯函数+树上dsu)

题意

给定一棵 n n n 个节点的树,每个节点上有一个点权,边权为均 1 1 1,求所有点权互质的点对路径总和。
1 ≤ n , 点权 ≤ 100000 1 \leq n,\text{点权} \leq 100000 1n,点权100000

思路

对于一个数 a a a,能与它产生贡献的数和 a a a 没有公共质因子,那我们可以通过筛出莫比乌斯系数,就可以用容斥的方法求出与 a a a 互质的部分了,如下图:
在这里插入图片描述
A , B , C A,B,C A,B,C a a a 的三个质因子,与 a a a 互质的部分就是上面的 U ∖ ( A ⋃ B ⋃ C ) U\setminus (A\bigcup B\bigcup C) U(ABC)
莫比乌斯系数筛法如下:

FOR(i,1,N)mo[i]=1,mark[i]=0;
FOR(i,2,N)if(!mark[i])
{
	for(int j=i;j<=N;j+=i)
	{
		mark[j]=1;
		if(j/i%i==0)mo[j]=0;
		else mo[j]=-mo[j];
	}
}

其中
μ ( x ) = {     1 if n is a square-free positive integer with an even number of prime factors − 1 if n is a square-free positive integer with an odd number of prime factors     0 if n has a squared prime factor. \mu(x){}=\begin{cases} \ \ \ 1\quad \text{if n is a square-free positive integer with an even number of prime factors}\\ -1\quad \text{if n is a square-free positive integer with an odd number of prime factors}\\ \ \ \ 0\quad \text{if n has a squared prime factor.} \end{cases} μ(x)=   1if n is a square-free positive integer with an even number of prime factors1if n is a square-free positive integer with an odd number of prime factors   0if n has a squared prime factor.
若一个数有一个因子是某个质数平方,那这个集合是某一个质数代表集合的子集,不会有贡献计算。即一个数表示成若干个质数的几次幂相乘时,这个幂次只能是 1 1 1。除此外,有偶数个质因子的莫比乌斯函数值是 1 1 1,奇数个为 − 1 -1 1
那我们不妨枚举 LCA \text{LCA} LCA,计算每一个父节点中符合条件的子节点对的贡献,单独对某个因子而言,两个同时是这个因子倍数的数将产生贡献,乘上莫比乌斯系数再累和就是所有的贡献。
贡献的算法只用化开式子即可,对于因子 s s s,某一个节点 u u u,给定所有节点的 l c a lca lca 时,对答案的贡献就是 m o s ∑ v Dis ( v , u ) mo_s\displaystyle\sum_v\text{Dis}(v,u) mosvDis(v,u) ,其中 u , v u,v u,v 属于 l c a lca lca 不同子节点的子树。
然后我们有 m o s ∑ v ( d e p u + d e p v − 2 d e p l c a ) mo_s\displaystyle\sum_v(dep_u+dep_v-2dep_{lca}) mosv(depu+depv2deplca)
s u m s sum_s sums 为目前全局数组中 s s s 倍数的节点深度总和, c n t s cnt_s cnts 为个数总和。
m o s ( s u m v + c n t s d e p u − 2 c n t s d e p l c a ) mo_s(sum_v+cnt_sdep_u-2cnt_sdep_{lca}) mos(sumv+cntsdepu2cntsdeplca)
维护好 s u m sum sum c n t cnt cnt 数组,每添加一棵子树就计算一次贡献即可。
把它改成 d s u dsu dsu 复杂度就变成 O ( n log ⁡ n ) O(n\log n) O(nlogn) 了。

#include<bits/stdc++.h>
#define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i)
#define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i)
typedef long long LL;
using namespace std;
const int N=1e5+3;
template<const int maxn,const int maxm>struct Linked_list
{
    int head[maxn],to[maxm],nxt[maxm],tot;
    Linked_list(){clear();}
    void clear(){memset(head,-1,sizeof(head));tot=0;}
    void add(int u,int v){to[++tot]=v,nxt[tot]=head[u],head[u]=tot;}
    #define EOR(i,G,u) for(int i=G.head[u];~i;i=G.nxt[i])
};
Linked_list<N,N<<1>G;
Linked_list<N,N*64>fac;
int mo[N];bool mark[N];
int L[N],R[N],ori[N],ord,dep[N],fa[N],sz[N],son[N],a[N],n;
LL cnt[N],sum[N];
LL ans;
 
void init()
{
    FOR(i,1,N-3)mo[i]=1,mark[i]=0;
    FOR(i,2,N-3)if(!mark[i])
    {
        for(int j=i;j<=N-3;j+=i)
        {
            mark[j]=1;
            if(j/i%i==0)mo[j]=0;
            else mo[j]=-mo[j];
        }
    }
    FOR(i,1,N-3)if(mo[i])
        for(int j=i;j<=N-3;j+=i)
            fac.add(j,i);
    return;
}
 
void dfs(int u,int f,int d)
{
    dep[u]=d,fa[u]=f,sz[u]=1,son[u]=0;
    L[u]=++ord,ori[ord]=u;
    EOR(i,G,u)
    {
        int v=G.to[i];
        if(v==f)continue;
        dfs(v,u,d+1);
        sz[u]+=sz[v];
        if(sz[v]>sz[son[u]])son[u]=v;
    }
    R[u]=ord;
}
 
void update(int l,int r,int k)
{
    FOR(i,l,r)
    {
        int u=ori[i];
        EOR(i,fac,a[u])
        {
            int s=fac.to[i];
            cnt[s]+=k;
            sum[s]+=k*dep[u];
        }
    }
}
void calc(int l,int r,int lca)
{
    FOR(i,l,r)
    {
        int u=ori[i];
        EOR(i,fac,a[u])
        {
            int s=fac.to[i];
            ans+=mo[s]*(sum[s]+cnt[s]*dep[u]-2*dep[lca]*cnt[s]);
        }
    }
}
 
void dsu(int u,int f)
{
    EOR(i,G,u)
    {
        int v=G.to[i];
        if(v==f||v==son[u])continue;
        dsu(v,u);update(L[v],R[v],-1);
    }
    if(son[u])dsu(son[u],u);
    EOR(i,G,u)
    {
        int v=G.to[i];
        if(v==f||v==son[u])continue;
        calc(L[v],R[v],u),update(L[v],R[v],1);
    }
    calc(L[u],L[u],u),update(L[u],L[u],1);
}
 
int main()
{
    init();
    scanf("%d",&n);
    FOR(i,1,n)scanf("%d",&a[i]);
    FOR(i,1,n-1)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        G.add(u,v),G.add(v,u);
    }
    dfs(1,0,1);
    dsu(1,0);
    printf("%lld\n",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值