首页 > 其他分享 >题解 - CF1972E - Divisors and Table

题解 - CF1972E - Divisors and Table

时间:2023-10-09 16:12:17浏览次数:31  
标签:CF1972E int 题解 sum operatorname MxN depth dist Divisors

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

注意:下文中根节点深度定义为 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);
}

总结

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

标签:CF1972E,int,题解,sum,operatorname,MxN,depth,dist,Divisors
From: https://www.cnblogs.com/WangManhe/p/17752002.html

相关文章

  • P4801题解
    解题思路:确实是一道很好的贪心,但由于加上了水这个影响因素,使题目复杂度上升了不少。(考虑的东西多了嘛)输个入。对饼干温度无脑排序。求最小值。求最大值(用双指针做,后面会讲)。解题过程:先输入(这个步骤就不用我讲了)inta[1000005];longlongn,ws;longlongmin......
  • Codeforces Round 902 (Div. 2) (CF1877) B、C、D 题解
    B题目大意你要传话给\(n\)个人,每传一下话需要花费\(p\),当一个人被传话后,他可以最多传给\(a_i\)个人,每次花费\(b_i\)。问把话传给\(n\)个人的最小花费。分析首先传给第一个人只少要\(p\)下来贪心,每次让花费最小、且能够传话的人去传话。考虑建一个堆,堆内的信息是......
  • 题解 尼克的任务
    有一种和题解区完全不同的做法。首先将所有任务按照时间从小到大排序,接着用\(f_i\)表示处理前\(i\)个任务所能得到的最大空闲时间。回顾一下需要满足的条件:再某个有任务的时刻,如果尼克是空闲的,就必须从中选择一个任务做。那么我们对于第\(i\)个任务,枚举上一个做的任务\(j......
  • P7710 [Ynoi2077] stdmxeypz 题解
    P7710[Ynoi2077]stdmxeypz题解我的第一道Ynoi题,体验感不高,调了大半天,最后发现有个地方\(B_1\)写成\(B_2\)了。分析树上问题,明显是要转到树下的,所以DFS序是一定要求的。有关树上距离,所以\(deep\)数组也是一定要求的。所以我们现在把问题转化成了:给你三个序列\(......
  • 「Round C10 B」间隔 题解
    简要题意本题有\(T\)组数据。给定一个由\(n\)个元素构成的正整数数列\(a_1,a_2,a_3...a_{n-1},a_n\)。问至少需要插入多少个整数才能使得\(a\)的各相邻元素之差相等(不能插入在头尾)。\(a\)数列保证是单调不减的。\(1\len\le10^6,0\lea_i\le10^6,1\leT\le1......
  • DBeaver [安装/问题解决]
     DBeaverMac版软件简介DBeaverMac版是一款专门为开发人员和数据库管理员设计的免费开源通用数据库工具。软件的易用性是它的宗旨,是经过精心设计和开发的数据库管理工具。免费、跨平台、基于开源框架和允许各种扩展写作(插件)。下载地址......
  • 【题解】CodeForces-1874/1875
    CodeForces-1875AJellyfishandUndertale一定是等待降到\(1\)或者能补满到\(a\)时才使用工具,依题意模拟即可。提交记录:Submission-CodeForcesCodeForces-1874AJellyfishandGame这种题目有点思路但是不是很会。赛时第一发写得根据奇偶性判断,\(k\)为偶数错了,然后感......
  • AtCoder Beginner Contest 323 (ABC 323) D、E、F 题解
    AtCoderBeginnerContest323(ABC323)D、E、F题解D题目大意给\(n\)种数\(s_i\),每一种数有\(c_i\)个,每次可以把两个相同的数合并为一个数,问最后会剩下多少数?分析对于每一个数\(s_i\),它最多被分解\(log_2c_i\)次,并且合并出来最大的数的大小小于\(s_i\timesc_i......
  • Hadoop问题解决(3)
    在启动hadoop过程中,出现如下错误:192.168.10.100:Invalidmaximumheapsize:-Xmx0m192.168.10.100:CouldnotcreatetheJavavirtualmachine.192.168.10.100:jobtracker已死,但pid文件仍存此时查看jobtracker的日志,1[root@ccloud100manager]#vim/var/log/hado......
  • hadoop问题解决(4)
    默认配置是将datanode,namenode,jobtracker,tasktracker,secondarynamenode的pid存放在/tmp目录下,随着linux的定期清理,这些pid就不见了,当然就无法停止了,怎么解决呢?在/tmp目录创建或者修改hadoop-hadoop用户名-datanode.pid 里面写入对应的pid, 可通过jps查看. namen......