北京理工大学小学期乐学 t23树上统计

写在前头

玩一下csdn的markdown。
这道题的大意是

给定一棵树,两个相邻点之间的距离为 1 1 1,对于相邻距离为 2 2 2的点之间可以新建一条边,要求每个点到其他所有点点的距离之和的和。

看似是一道搜索题,但是我多日后再想起来感觉还有动态规划的思路在里头。

t23

参考帖子:http://lexue.bit.edu.cn/mod/forum/discuss.php?d=126124,阅读前请先看一看这篇文章。

如果看不到原帖,这里大致概括本贴中用到的原帖中的结论,对于相邻的节点 i , j i,j i,j,原帖总结了所有节点中除了 i , j i,j i,j 外的任意节点 x x x i i i 的距离 d d d 于到 j j j 的距离 d ′ d' d 之间的关系。

本文是对上贴中最后留白部分的推论,我写这个的时候程序还没写(主要是担心写不出ac,先写写推导过程拿一点平时分orz)

约定

在zzxdl的帖子中两点之间的距离用 d i s t dist dist 表示,文中用 d d d 表示,且 d ( x , x ) = 0 , d ′ ( x , x ) = 0 d(x,x)=0,d'(x,x)=0 d(x,x)=0,d(x,x)=0

正文

对于图 G ( E , V ) G(E,V) G(E,V),取一条边 i − j i-j ij,边上的两点分别为 i , j i,j i,j,设节点 i i i 的所有子树编号为 1 , … , n 1,\ldots,n 1,,n j j j 节点所有子树编号为 1 , … , m 1,\ldots,m 1,,m,对于 i i i 节点的第 k k k 个子树上所有节点的集合为 S i k S_{ik} Sik,其中所有到 i i i 距离为偶数的节点的集合为 E i k E_{ik} Eik ,距离为奇数的节点的集合为 O i k O_{ik} Oik。于节点 j j j 同理。

那么有:
⋃ k E j k = ⋃ k O i k − { j } ⋃ k O j k = ⋃ k E i k ∪ { i } \begin{aligned} \bigcup_k{E_{jk}}=\bigcup_k{O_{ik}}-\{j\}\\ \bigcup_k{O_{jk}}=\bigcup_k{E_{ik}}\cup\{i\}\\ \end{aligned} kEjk=kOik{j}kOjk=kEik{i}
令:
O i = ⋃ k O i k O j = ⋃ k O j k E i = ⋃ k E i k E j = ⋃ k E j k s u m i = ∑ u ≠ i d ′ ( u , i ) s u m j = ∑ u ≠ j d ′ ( u , j ) \begin{aligned} &O_i=\bigcup_k{O_{ik}} & O_j=\bigcup_k{O_{jk}}\\ &E_i=\bigcup_k{E_{ik}} & E_j=\bigcup_k{E_{jk}}\\ &sum_i=\sum_{u\neq i}{d'(u,i)}\\&sum_j=\sum_{u\neq j}{d'(u,j)} \end{aligned} Oi=kOikEi=kEiksumi=u=id(u,i)sumj=u=jd(u,j)Oj=kOjkEj=kEjk
显然,结合上文的推导:
s u m i = ∑ u ∈ E i d ′ ( u , i ) + ∑ u ∈ O i d ′ ( u , i ) s u m j = ∑ u ∈ E j d ′ ( u , j ) + ∑ u ∈ O j d ′ ( u , j ) = ∑ u ∈ O i − { j } d ′ ( u , j ) + ∑ u ∈ E i + { i } d ′ ( u , j ) = ∑ u ∈ O i d ′ ( u , j ) + ∑ u ∈ E i d ′ ( u , j ) + 1 \begin{aligned} sum_i&=\sum_{u\in E_i}{d'(u,i)}+\sum_{u\in O_i}{d'(u,i)}\\ sum_j&=\sum_{u\in E_j}{d'(u,j)}+\sum_{u\in O_j}{d'(u,j)}\\ &=\sum_{u\in O_i-\{j\}}{d'(u,j)}+\sum_{u\in E_i+\{i\}}{d'(u,j)}\\ &=\sum_{u\in O_i}{d'(u,j)}+\sum_{u\in E_i}{d'(u,j)}+1\\ \end{aligned} sumisumj=uEid(u,i)+uOid(u,i)=uEjd(u,j)+uOjd(u,j)=uOi{j}d(u,j)+uEi+{i}d(u,j)=uOid(u,j)+uEid(u,j)+1
由于 ( i , j ) ∈ E (i,j)\in E (i,j)E,所以有且仅有唯一的 p ∈ { 1 , 2 … , n } p\in\{1,2\ldots,n\} p{1,2,n},使得其所对应的子树中所有点的集合满足:
j ∈ S i p E i p + O i p = S i p \begin{aligned} j&\in S_{ip}\\ E_{ip}+O_{ip}&=S_{ip} \end{aligned} jEip+OipSip=Sip
那么:
s u m j = ∑ u ∈ O i − O i p d ′ ( u , j ) + ∑ u ∈ E i − E i p d ′ ( u , j ) + 1 + ∑ u ∈ O i p d ′ ( u , j ) + ∑ u ∈ E i p d ′ ( u , j ) = ∑ u ∈ O i − O i p d ′ ( u , i ) + ∑ u ∈ E i − E i p d ′ ( u , i ) + 1 + ∣ E i ∣ − ∣ E i p ∣ + ∑ u ∈ O i p d ′ ( u , i ) − ∣ O i p ∣ + ∑ u ∈ E i p d ′ ( u , i ) = ∑ u ∈ E i d ′ ( u , i ) + ∑ u ∈ O i d ′ ( u , i ) + ∣ E i ∣ − ∣ S i p ∣ + 1 = s u m i + ∣ E i ∣ − ∣ S i p ∣ + 1 \begin{aligned} sum_j&=\sum_{u\in O_i-O_{ip}}{d'(u,j)}+\sum_{u\in E_i-E_{ip}}{d'(u,j)}+1+\sum_{u\in O_{ip}}{d'(u,j)}+\sum_{u\in E_{ip}}{d'(u,j)}\\ &=\sum_{u\in O_i-O_{ip}}{d'(u,i)}+\sum_{u\in E_i-E_{ip}}{d'(u,i)}+1+|E_i|-|E_{ip}|+\sum_{u\in O_{ip}}{d'(u,i)}-|O_{ip}|+\sum_{u\in E_{ip}}{d'(u,i)}\\ &=\sum_{u\in E_i}{d'(u,i)}+\sum_{u\in O_i}{d'(u,i)}+|E_i|-|S_{ip}|+1\\ &=sum_i+|E_i|-|S_{ip}|+1 \end{aligned} sumj=uOiOipd(u,j)+uEiEipd(u,j)+1+uOipd(u,j)+uEipd(u,j)=uOiOipd(u,i)+uEiEipd(u,i)+1+EiEip+uOipd(u,i)Oip+uEipd(u,i)=uEid(u,i)+uOid(u,i)+EiSip+1=sumi+EiSip+1

由此再进行一次深度优先搜索即可得到答案。

贴上代码

#include <iostream>
#include <vector>
using namespace std;

#define N 200 // 2000000
#define HALF(x) (((x)&1) + ((x)>>1)) // 除二并向上取整

vector<int> edge[N];
/*
oddR 所有节点到根节点的奇数点的个数
evenR 所有节点到根节点的偶数点的个数
sizeTree 以r为根的数中每一个几点所代表的子树的节点个数
*/
int oddR, evenR, sizeTree[N];
int disRoot[N]; // 节点到根的距离
long long sum[N], sumUp;

int E(int i); // 到节点i的距离为偶数的点的个数
int O(int i); // 到节点i的距离为奇数的点的个数
int dfs1(int i, int f); // 给出所有点的子树的规模
void dfs2(int i, int f, int r, int depth); // 给出所有点的奇偶节点的数量
void dfs3(int i, int f); // 转移计算

int main(void)
{
    int n, a, b, r;
    cin >> n;
    while (n-- > 1)
    {
        scanf("%d%d", &a, &b); getchar();
        edge[a].push_back(b);
        edge[b].push_back(a);
    }
    r = a;
    dfs1(r, 0);
    dfs2(r, 0, r, 1);
    sumUp += sum[r];
    dfs3(r, 0);
    cout << sumUp / 2 << endl;
    return 0;
}

void dfs3(int i, int f)
{
    for (int j = 0; j < edge[i].size(); j++)
    {
        int tmp = edge[i][j];
        if (tmp != f)
        {
            sum[tmp] = sum[i] + E(i) - sizeTree[tmp] + 1;
            sumUp += sum[tmp];
            dfs3(tmp, i);
        }
    }
    return;
}

int E(int i) { return disRoot[i] % 2 == 0 ? evenR : oddR - 1; }
int O(int i) { return disRoot[i] % 2 == 0 ? oddR : evenR + 1; }

void dfs2(int i, int f, int r, int depth)
{
    for (int j = 0; j < edge[i].size(); j++)
    {
        int tmp = edge[i][j];
        if (tmp != f)
        {
            if (depth & 1 == 1) // 距离根节点奇数距离
                oddR++;
            else evenR++;  // 距离根节点偶数距离
            disRoot[tmp] = depth;
            sum[r] += HALF(depth);
            dfs2(tmp, i, r, depth + 1);
        }
    }
    return;
}

int dfs1(int i, int f)
{
    for (int j = 0; j < edge[i].size(); j++)
    {
        int tmp = edge[i][j];
        if (tmp != f)
            sizeTree[i] += dfs1(tmp, i);
    }
    sizeTree[i]++;
    return sizeTree[i];
}

  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
北京理工大学成立于1940年,是中国一所以工科为主、工、理、管、文、法等多学科协调发展的综合性大学。乐学作业是北京理工大学的一种独特教育理念,强调学生在学习过程中的快乐体验和有效成果。 乐学作业的核心理念是让学生从传统的单一教学模式中解放出来,培养积极主动的学习态度和兴趣。同时,乐学作业也倡导理论与实践相结合,鼓励学生动手实践,锻炼解决问题的能力。 在乐学作业中,学生可以根据自己的兴趣和特长选择适合自己的课程和项目。学校提供了丰富的学科和专业,包括工程、科学、管理、文学、法学等多个领域。学生可以通过多种途径获取知识,例如课堂教学、实验研究、社会实践等。 乐学作业的教学方法也非常灵活多样。老师会根据学生特点和需求提供个性化指导,帮助学生克服学习困难。同时,学生在团队合作中也可以相互学习、相互促进。学校还注重培养学生的创新思维和实践能力,鼓励学生进行创新性的研究和项目实践。 乐学作业的目的是培养具备批判性思维、创新意识和实践能力的综合型人才。在这种教育理念下,学生可以更主动地参与学习,享受到学习的乐趣,并且能够更好地适应社会的发展和变化。 作为一所享有盛誉的高校,北京理工大学乐学作业模式不仅在国内受到关注和推崇,也赢得了国际上的广泛认可。它为学生提供了全面的教育平台,培养了一批才华横溢的年轻人,为社会的进步和发展做出了重要贡献。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值