题解 - CF1972E - Divisors and Table

这题正解是虚树,本解法卡常,仅适合不会虚树的。(例如本人)

注意:下文中根节点深度定义为 1 .

第一步: 转化问题

我们把 $ g(x,y,z) $ 拆开,考虑每个质数是哪些点的因子。

包含这个质数的点构成一个点集,我们只需求这个点集 S 的 $ \sum\limits_{x,y,z\in S } f(x,y,z) $。

第二步: 对于已知点集 S 的 $ \sum\limits_{x,y,z\in S } f(x,y,z) $

首先考虑三个点构成的最小连通块的边数。

这可以简单计算出:

\[\dfrac{\operatorname{dist}(u,v)+\operatorname{dist}(v,w)+\operatorname{dist}(u,w)}{2} \]

通过式子可以算出,点集内每两个点的距离恰好被计算 $ \frac{|S|-2}{2} $ 次。

通过某种方法计算 $\sum\limits_{x,y\in S } \operatorname{dist}(x,y) $ ,乘上 $ \frac{|S|-2}{2} $ 获得答案。

第三步: "某种方法"

我们需要求:

\[\sum\limits_{x,y\in S } \operatorname{dist}(x,y) \]

使用树上差分:

\[\operatorname{dist}(x,y) = \operatorname{depth}(x) + \operatorname{depth}(y) - 2 \operatorname{depth}(\operatorname{lca}(u,v)) \]

带回求和得到

\[ \begin{aligned} \sum\limits_{x,y\in S } \operatorname{dist}(x,y) = \sum\limits_{x,y\in S } \operatorname{depth}(x) + \operatorname{depth}(y) - 2 \operatorname{depth}(\operatorname{lca}(u,v))\\ \end{aligned} \]

我们依次加入点,通过记录前面的点的深度和以及前面的点的个数,可以简单处理这个式子的前两块。

对于包含 LCA 的深度的部分,我们通过数据结构可以处理。

加入一个点的时候,给这个点到根节点的所有点点权加上一,查询一个点的时候,查询这个点到根节点的点权和,这就是这个点与之前所有点的 LCA 的深度和。

为什么呢?

考虑 LCA 的深度在树上的实际意义,就是两点到根的路径的重合点数,然后就好理解了。

代码

本题卡常,代码中使用了树状数组而不是线段树。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MOD=998244353;
const int MxN=200000;
int n,Answer,result,TotalDepth,Count;
int head[MxN+5],nxt[2*MxN+5],to[2*MxN+5],tot;
int siz[MxN+5],depth[MxN+6],son[MxN+5],top[MxN+5],father[MxN+5];
int L[MxN+5],R[MxN+5],rnk[MxN+5],tim;
int value[MxN+5];
vector<int>Ver[MxN+5];
int Pow(int A,int B){
    int res=1;
    while(B){
        if(B&1){
            res=res*A%MOD;
        }
        A=A*A%MOD;
        B>>=1;
    }
    return res;
}
void AddEdge(int u,int v){
    to[++tot]=v;
    nxt[tot]=head[u];
    head[u]=tot;
}
void DFS1(int p,int fa){
    siz[p]=1;
    father[p]=fa;
    depth[p]=depth[fa]+1;
    for(int i=head[p];i;i=nxt[i]){
        if(to[i]!=fa){
            DFS1(to[i],p);
            siz[p]+=siz[to[i]];
            if(siz[to[i]]>siz[son[p]]){
                son[p]=to[i];
            }
        }
    }
}
void DFS2(int p,int fa,int tp){
    L[p]=++tim;
    rnk[tim]=p;
    top[p]=tp;
    if(son[p]){
        DFS2(son[p],p,tp);
    }
    for(int i=head[p];i;i=nxt[i]){
        if(to[i]!=fa&&to[i]!=son[p]){
            DFS2(to[i],p,to[i]);
        }
    }
    R[p]=tim;
}
struct BIT{
    int T[MxN+5],TT[MxN+5];
    void Modify(int l,int r,int c){
        for(int i=l;i<=n;i+=i&-i){T[i]+=c;TT[i]+=c*l;}
        for(int i=(r+1);i<=n;i+=i&-i){T[i]-=c;TT[i]-=c*(r+1);}
    }
    int Query(int l,int r){
        int res=0;
        for(int i=r;i;i-=i&-i){res+=(r+1)*T[i];res-=TT[i];}
        for(int i=l-1;i;i-=i&-i){res-=(l)*T[i];res+=TT[i];}
        return res;
    }
}bit;
void Insert(int u){
    int res=TotalDepth+Count*depth[u],v=u;
    while(v){
        res=(res-2*bit.Query(L[top[v]],L[v])%MOD+MOD)%MOD;
        bit.Modify(L[top[v]],L[v],1);
        v=father[top[v]];
    }
    TotalDepth=(TotalDepth+depth[u])%MOD;
    Count=(Count+1)%MOD;
    result=(result+res)%MOD;
}
void Delete(int u){
    int v=u;
    while(v){
        bit.Modify(L[top[v]],L[v],-1);
        v=father[top[v]];
    }
}
void Calculate(int num){
    result=0;TotalDepth=0;Count=0;
    for(auto i:Ver[num]){
        Insert(i);
    }
    Answer=(Answer+result*(Count-2)%MOD*Pow(2,MOD-2)%MOD)%MOD;
    for(auto i:Ver[num]){
        Delete(i);
    }
}
signed main(){
    int u,v;
    scanf("%lld",&n);
    for(int i=1;i<=n;i++){
        scanf("%lld",&value[i]);
        int t=value[i];
        for(int j=2;j*j<=t;j++){
            if(t%j==0){
                Ver[j].push_back(i);
                while(t%j==0){
                    t/=j;
                }
            }
        }
        if(t>1){
            Ver[t].push_back(i);
        }
    }
    for(int i=1;i<n;i++){
        scanf("%lld%lld",&u,&v);
        AddEdge(u,v);
        AddEdge(v,u);
    }
    DFS1(1,0);
    DFS2(1,0,1);
    for(int i=2;i<=MxN;i++){
        Calculate(i);
    }
    printf("%lld\n",Answer);
}

总结

使用了很顺畅的思路流程,不是正解而且卡常,但是好理解才是重要的。 😄

posted @ 2023-10-09 16:02  WangManhe  阅读(43)  评论(0)    收藏  举报