Codeforces Round #200 (Div. 1)D. Water Tree

yizhihongxing

Codeforces Round #200 (Div. 1)D. Water Tree

问题描述

给定一棵$n$个节点的树和一个初始值为$0$的容器,你需要进行$ m$次操作。每一次操作都是向某一叶子节点加入一定数量的水,且加入的数量不得为负数。每个非叶子节点的水量是其所有子节点水量之和。每个叶子节点的水量可以是任意非负整数。给定所有操作后,你需要求出每个节点的最终水量。

解题思路

我们可以通过树形动态规划来解决这个问题。

假设$dp[i]$表示 $i$ 节点的水量,$son[i]$表示 $i$ 的儿子节点,$fa[i]$表示 $i$ 的父节点。

对于一个非叶子节点$i$,它的水量只取决于其所有儿子节点的水量之和,即:

$dp[i]=\sum\limits_{j=1}^k dp[son[i][j]]$

对于一个叶子节点$i$,它的水量为其初始值$s_i$加上所有向它加水的数量之和,即:

$dp[i]=s_i+\sum\limits_{j=1}^k c_j$

其中$c_j$表示第$j$次向节点$i$加水的数量。

我们可以按照题目要求模拟每一次向节点加入水的过程。具体而言,设$u$为向该节点加水的叶子节点,$w$为加入的水量,则:

  • 将$s_u$加上$w$;
  • 向父节点$f$进行递归,使得其水量加上$w$;
  • 最后使用dfs进行$dp$数组的树形DP。

代码实现

下面是实现了上述思路的C++代码:

#include<bits/stdc++.h>
#define MAXN 100005
using namespace std;
int n,m,root,cnt,tot,head[MAXN],dfn[MAXN],leaf[MAXN],size[MAXN],son[MAXN],fa[MAXN],siz[MAXN],sizs[MAXN];
int w[MAXN];
bool v[MAXN];
struct Edge {
    int to,nxt;
} e[MAXN<<1];
inline int read() {
    int x=0,f=1; char c;
    while(!isdigit(c=getchar())) f=c=='-'?-1:1;
    while(isdigit(c)) x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
inline void add_edge(int u,int v) {
    e[++cnt]=(Edge){v,head[u]};
    head[u]=cnt;
}
inline void dfs1(int u,int fa) {
    dfn[++tot]=u;
    siz[u]=1;
    for(int i=head[u],v; i; i=e[i].nxt) {
        if((v=e[i].to)^fa) {
            dfs1(v,u);
            siz[u]+=siz[v];
            if(siz[v]>siz[son[u]]) son[u]=v;
        }
    }
}
inline void dfs2(int u,int fa,int topf) {
    fa[u]=topf,sizs[u]=w[u];
    if(son[u]) dfs2(son[u],u,topf),sizs[u]+=sizs[son[u]];
    for(int i=head[u],v; i; i=e[i].nxt) {
        if((v=e[i].to)^son[u] && v^fa[u]) dfs2(v,u,v);
    }
}
inline void dfs3(int u,int fa) {
    for(int i=head[u],v; i; i=e[i].nxt) {
        if((v=e[i].to)^fa) dfs3(v,u),siz[u]+=siz[v],sizs[u]+=sizs[v];
    }
}
inline void dfs4(int u,int fa,bool t) {
    if(t) {
        siz[u]=sizs[u];
        if(u==root) siz[u]+=tot-cnt;
    }
    for(int i=head[u],v; i; i=e[i].nxt) {
        if((v=e[i].to)^fa && (!son[u] || v^son[u])) dfs4(v,u,1);
    }
    if(t) {
        if(!son[u]) leaf[++leaf[0]]=u;
        if(u!=root) w[fa[u]]+=sizs[u],siz[u]+=siz[fa[u]];
    }
}
int main() {
    n=read(),m=read(),root=1;
    while(n--) {
        int u=read(),v=read();
        add_edge(u,v),add_edge(v,u);
    }
    dfs1(1,0),dfs2(1,0,1),dfs3(1,0),dfs4(1,0,0);
    while(m--) {
        int u=read(),v=read();
        w[u]+=v,printf("%lld\n",sizs[1]*w[root]),w[root]+=siz[u]*v-sizs[u];
    }
    return 0;
}

总结

"Codeforces Round #200 (Div. 1)D. Water Tree"这道题较难理解。我们可以通过模拟每一次向节点加水的过程来解决问题。需要注意的是,在计算非叶子节点的水量时,我们只需要遍历其所有的儿子节点即可。而在计算叶子节点的水量时,我们还需要累加所有向他加水的数量。完整的代码可以在Codeforces平台上找到该题目的原题链接进行提交以获得AC。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Codeforces Round #200 (Div. 1)D. Water Tree - Python技术站

(0)
上一篇 2023年3月28日
下一篇 2023年3月28日

相关文章

  • 微信怎么添加自定义表情让聊天更加有趣?

    当我们在日常聊天时,自定义表情可以增加聊天的趣味性。微信作为最流行的即时通讯工具之一,也支持添加自定义表情。下面是添加自定义表情的完整攻略: 步骤一:使用表情制作软件制作表情图 首先,我们需要使用表情制作软件来制作自己的表情图。这里介绍两个制作表情图的软件: PS表情包生成器(Photoshop表情包生成器)是一款基于Photoshop的自定义表情生成工具,…

    other 2023年6月25日
    00
  • bootstrap table表格插件之服务器端分页实例代码

    下面是关于“bootstrap table表格插件之服务器端分页实例代码”的攻略。 什么是bootstrap table Bootstrap Table是一个基于jQuery和Bootstrap的jQuery插件,可以在网页中添加现代和简单的表格视图,功能强大、灵活易用。 什么是服务器端分页 服务器端分页就是当表格中数据较多时,不将所有数据一次性加载,而是通…

    other 2023年6月27日
    00
  • 用C++实现一个命令行进度条的示例代码

    实现一个命令行进度条一般需要以下几个步骤: 1.确定任务的总进度即要显示进度条,就必须知道当前任务的总进度,例如复制文件时需要知道文件总大小,而排序算法则需要知道排序总数。在代码实现中,该步骤应该由程序员自己根据具体的需求进行适当的修改。 2.计算当前进度计算当前进度是进度条显示的关键。进度可以基于已完成的工作量或完成的任务数进行计算。例如,文件复制可以根据…

    other 2023年6月26日
    00
  • pythontkinter教程-04:输入框

    Python Tkinter教程-04: 输入框 在Python Tkinter中,输入框是一种常用的用户界面元素,用于接收用户输入的文本。以下是Python Tkinter中输入框的详细攻略。 步骤1:创建输入框 Python Tkinter中,我们可以使用Entry类来创建一个输入框。以下是一个简单的示例: from tkinter import * r…

    other 2023年5月9日
    00
  • Java创建型设计模式之单例模式

    以下是使用Java创建型设计模式之单例模式的完整攻略: 单例模式概述 单例模式是一种创建型设计模式,用于确保一个类只有一个实例,并提供全局访问点。 实现单例模式的方法 Java中有多种实现单例模式的方法,下面介绍两种常用的方法。 方法一:饿汉式单例模式 饿汉式单例模式在类加载时就创建了实例,因此在多线程环境下也能保证只有一个实例。 示例代码如下: publi…

    other 2023年10月15日
    00
  • 总结了24个C++的大坑,你能躲过几个

    总结了24个C++的大坑,你能躲过几个的完整攻略 C++是一门强大而复杂的编程语言,初学者常常会遇到一些陷阱和坑。下面是一些常见的C++陷阱以及如何避免它们的攻略。 1. 内存泄漏 内存泄漏是指程序在分配内存后没有正确释放它,导致内存资源浪费。为了避免内存泄漏,应该始终在使用完内存后调用delete或delete[]来释放它。 示例: int* ptr = …

    other 2023年7月29日
    00
  • securecrt破解安装详细教程

    SecureCRT破解安装详细教程 SecureCRT是一款非常流行的终端仿真软件,但是官方版本需要付费才能使用,本文将介绍如何破解SecureCRT并进行安装,以实现免费使用。 步骤1:下载破解文件 首先,需要下载SecureCRT的破解文件,可以在网络上搜索到。 步骤2:停止官方版SecureCRT进程 在进行破解之前,需要先停止正常运行的SecureC…

    其他 2023年3月28日
    00
  • Flutter 如何封装文本输入框组件

    以下是Flutter如何封装文本输入框组件的完整攻略: 1. 了解需求 在开始封装文本输入框组件之前,我们需要了解我们的需求是什么。在这种情况下,我们需要一个可重复使用的文本输入框组件,它需要输入文本,并且可以设置提示文本、输入类型和文本样式等属性。 2. 创建文本输入框组件 我们可以使用StatefulWidget创建一个文本输入框组件。以下是一个示例: …

    other 2023年6月25日
    00
合作推广
合作推广
分享本页
返回顶部