问题回顾
看到很多人说这是小学期最难的一道题,在这里表示同感。
关于图和树
首先我们需要对树和图的知识有一个基本的了解:
- 连通图表示图中任意两个节点之间都有一条或多条线段连接,并且n个节点要构成连通图至少需要n-1条边(每次添加一条边都要连接新的节点)。
- 这题恰好是有n-1条边和n个节点,容易想到图里一定没有环,所以这道题看似是图,其实所有节点构成的是树 (树上统计的名字由此而来)。
- 简而言之,n个节点用n-1条边连成的无向连通图便是树。
问题1
先考虑第一个问题,即求Σdist(i,j)。
显然暴力枚举所有成对的节点(复杂度O(n^2))是不可接受的。考虑到n的数量级,我们想到本题可能是要对边进行遍历,则不难想到统计每条边被通过的次数,叠加起来便是距离总和(每条边长均为1)。
基于树的结构,每条边被通过的次数等于其左边节点总数乘以右边节点总数(而由于n已知,左右节点数我们只要算出其一即可)。显然左右节点数即为左右子树的大小。对于每一个节点,我们要去计算该节点子树的大小,只要去计算其子节点子树的大小再加上其自身,这样反复递归调用,直至搜索到叶子结点,然后再回溯,便可计算出所有节点子树的大小。
显然我们要从根节点向子节点方向搜索,而对于一棵树,其中每一个节点都可以作为树的根节点(我们实操过程中不妨假设根节点为1),一旦指定了根节点,我们可以标记访问过的节点,这样便可以确保是一直在向子节点访问。这样我们计算出了每一条边两端节点数,便可计算出该边被通过次数,从而得到问题1的答案。
问题2
再考虑升级后的版本,即在原有的每两个距离为2的节点间再添加一条边。多举几个例子,我们不难发现经处理后,原来偶数距离会变成原来的一半,奇数距离是原来的一半向上取整,但是总距离并不是简单的二倍关系。
这里关键之处是要想到:新的总距离 = 原偶数距离总和/2+(奇数距离总和+奇数距离数目)/2。显然前后两项都是整除,所以我们可以将其合并在一起,得到关系式:新的总距离=(原总距离+奇数距离数目)/2,所以我们只是要计算在原来图中,有多少对节点之间的距离是奇数。
对于节点u,v,显然两者距离等于两者到最近公共祖先(LCA)距离之和,假定深度为dep,有如下关系:dep(u)+dep(v)-2dep(LCA)=dist(u,v)。由于等号左边-2dep(LCA)项显然为偶数,我们只要考虑dep(u)和dep(v)便可确定dist(u,v)是否为奇数。所以我们只要在原有计算子树大小的函数中,一并计算每个节点的深度(或者只记录每个节点的深度是否为奇数),最终便可确定每两点间距离是否为奇数。最终便可计算出新的总距离。
后记
再提一个坑点,两个较大的整数相乘可能会爆int,这时要考虑使用(unsigned)long long。
最后,对于这种输入数据量比较大的题目,采用快读可以明显减少运行时间(本人最后一个样例就是在用了快读之后才过的)。
当然,这只是我的解法,如果大家有别的方法,欢迎分享交流。