啊啊啊啊啊啊啊啊考场上差一点就A掉了5555

千里之堤溃于蚁穴……鬼知道最后一步那么显然的柿子我为什么没考虑用上……

观察数据范围可知,出题人期望我们想出一个$O(n)$的做法

当然也有可能是$O(nlogn)$,但是这道题所求的数值与树上每个点的权值有关,

似乎用点分治并不能够解决。

那怎么办?树形dp啊。保证严格$O(n)$。

有了这样的思路,我们先来看第一问,并设计一个可以用一遍dfs计算出数组$b[]$的算法。

各位想必知道,树形dp的基本思想是$"Up\ and\ Down"$,

而在最近的比赛和专题中,我们似乎见的大部分这类问题都是先向下dfs到底,再从下往上更新父亲的$dp[]$信息。

但是不要忘了另外一种啊喂……这题先不说从下往上能不能转移,就是起始更新点的初值你都没法$O(n)$以内算……

如果从上往下,用父亲更新儿子就十分好考虑了。

首先,起始点即为根节点,初值用一遍dfs即可算出。

之后考虑怎么转移。假设我们已知$b[2]$,要求它的儿子5的$b[]$,

这时候先来想一下$O(n^2)$的做法,即对于每个节点都跑一遍dfs,它究竟输在了哪里?

显然,有边相连的两点的$b[]$是有关系的,可以通过某种方式转化,而不必每次遍历整棵树进行冗余计算。

从2到5,5的子树(包含5本身)对于$b[]$的贡献都少了1,而5的子树之外的部分对它的贡献都多了1,

再通俗一点,我们把5的子树的贡献写出来:$b[2]=a_5+2*a_{10}+2*a_{11}...(之后就是5的子树之外的部分)$

而$b[5]=a_5*0+1*a_{10}+1*a_{11}+...$

看见没有?系数都少了1!子树外的部分同理。

那么一棵子树的贡献可以看作它内部点的权值和,第一遍dfs的时候就能预处理完毕。设它为$sum[]$。

易得:$b[y]=b[x]-sum[y]+sum[1]-sum[y]=b[x]+sum[1]-sum[y]*2$

第一问解决。

得出上面那个式子之后,第二问也就非常显然了。

我们把$b[x]$移到左侧,得到$b[y]-b[x]=sum[1]-sum[y]*2$

设$dt[y]=b[y]-b[x]$,这玩意相当于题里已经给了可以马上算出来。(当然$dt[1]$肯定求不出来)

先求出每个点的$dt[y]=sum[1]-sum[y]*2$,之后想怎么能够消元。

显然,$b[1]=\sum \limits _{i=2}^{n} sum[i]$

什么?并不显然?自己手玩去!

然后我们令$total=\sum \limits _{i=2}^{n}{dt[i]}=(n-1)sum[1]-2*\sum \limits_{i=2}^{n}{sum[i]}$

发现了什么?因为$b[1]$是已知量,所以可以把$total$减号后面的部分消掉!

$sum[1]$就求出来辣!

又因为所有的$dt[]$都是已知的,所有的$sum[]$就都可以求出来了。

至于$a[]$?再来一遍dfs就行了。

不得不说这道题真的很棒,没有特别难的知识点,整体难度也不太高,

但是很考验选手对于树上信息的处理能力和转化能力,以及基本的数学素养。

部分分也给的很合理,还能考察一下高斯消元。

还在犹豫什么?还不快来%%%% @liu_runda (逃

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int N=;
typedef long long ll;
int read()
{
int x=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<='')x=x*+ch-'',ch=getchar();
return x*f;
}
int to[N<<],nxt[N<<],tot,head[N],num[N],sum[N],dep[N];
int dt[N];
int T,n,op;
int dp[N];
void add(int x,int y)
{
to[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
void pre(int x,int deep)
{
sum[x]=num[x];dep[x]=deep;
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];
if(dep[y]||y==)continue;
pre(y,deep+);
sum[x]+=sum[y];
}
}
void dfs(int x)
{
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];
if(dep[y]<dep[x])continue;
dp[y]=dp[x]-sum[y]+sum[]-sum[y];
dfs(y);
}
}
void DFS(int x,int f)
{
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];
if(y==f)continue;
dt[y]=dp[y]-dp[x];
DFS(y,x);
}
}
void cacl()
{
for(int i=;i<=n;i++)
num[i]=read();
pre(,);
for(int i=;i<=n;i++)
dp[]+=dep[i]*num[i];
dfs();
for(int i=;i<=n;i++)
printf("%d ",dp[i]);
printf("\n");
}
void getans(int x,int f)
{
ll ssum=;
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];
if(y==f)continue;
getans(y,x);
ssum+=sum[y];
}
num[x]=sum[x]-ssum;
}
void solve()
{
for(int i=;i<=n;i++)
dp[i]=read();
DFS(,);
ll total=;
for(int i=;i<=n;i++)
total+=1LL*dt[i];
sum[]=1LL*(dp[]*+total)/(n-);
for(int i=;i<=n;i++)
sum[i]=(-dt[i]+sum[])/;
getans(,);
for(int i=;i<=n;i++)
printf("%d ",num[i]);
printf("\n");
}
void ini()
{
for(int i=;i<=n*;i++)
{
to[i]=nxt[i]=;
if(i<=n)dp[i]=dt[i]=head[i]=sum[i]=num[i]=dep[i]=;
}
tot=;
}
void work()
{
n=read();
ini();
for(int i=;i<n;i++)
{
int x=read(),y=read();
add(x,y);add(y,x);
}
op=read();
if(op)solve();
else cacl();
}
int main()
{
T=read();
while(T--)work();
return ;
}

UPD:附赠两组样例


Sample

两组是对称的(一组输入为另一组输出)

[20190727NOIP模拟测试9]单(single) 题解(树上dp)的更多相关文章

  1. 洛谷【P2458】[SDOI2006]保安站岗 题解 树上DP

    题目描述 五一来临,某地下超市为了便于疏通和指挥密集的人员和车辆,以免造成超市内的混乱和拥挤,准备临时从外单位调用部分保安来维持交通秩序. 已知整个地下超市的所有通道呈一棵树的形状:某些通道之间可以互 ...

  2. [CSP-S模拟测试]:Cicada与排序(概率DP)

    题目传送门(内部题93) 输入格式 第一行一个整数$n$,代表数列的长度. 接下来一行$n$个数$a_i$,用空格分隔开. 输出格式 输出一行$n$个数,表示原数列上这个位置在执行后的期望位置,注意输 ...

  3. [CSP-S模拟测试]:点亮(状压DP+树上背包DP)

    题目传送门(内部题121) 输入格式 第一行,一个正整数$n$. 第二行,$n-1$个正整数$p_2,p_3,...,p_n$.保证$p_u$是在$1$到$u-1$中等概率随机选取的. 接下来$n$行 ...

  4. [CSP-S模拟测试]:电压机制(图论+树上差分)

    题目描述 科学家在“无限神机”($Infinity\ Machine$)找到一个奇怪的机制,这个机制有$N$个元件,有$M$条电线连接这些元件,所有元件都是连通的.两个元件之间可能有多条电线连接.科学 ...

  5. [CSP模拟测试43、44]题解

    状态极差的两场.感觉现在自己的思维方式很是有问题. (但愿今天考试开始的一刻我不会看到H I J) A 考场上打了最短路+贪心,水了60. 然而正解其实比那30分贪心好想多了. 进行n次乘法后的结果一 ...

  6. [7.18NOIP模拟测试5]砍树 题解(数论分块)

    题面(加密) 又考没学的姿势……不带这么玩的…… 考场上打了个模拟 骗到30分滚粗了 稍加思考(滑稽)可将题面转化为: 求一个最大的$d$,使得 $\sum \limits _{i=1}^n {(\l ...

  7. [NOIP模拟测试3] 建造游乐园 题解(欧拉图性质)

    Orz 出题人石二队爷 我们可以先求出有n个点的联通欧拉图数量,然后使它删或增一条边得到我们要求的方案 也就是让它乘上$C_n^2$ (n个点里选2个点,要么删边要么连边,选择唯一) 那么接下来就是求 ...

  8. [NOIP模拟测试9]题(Problem) 题解 (组合数全家桶+dp)

    达哥送分给我我都不要,感觉自己挺牛批. $type=0:$ 跟visit那题类似,枚举横向移动的步数直接推公式: $ans=\sum C_n^i \times C_i^{\frac{i}{2}} \t ...

  9. [CSP-S模拟测试]:任务分配(最短路+贪心+DP)

    题目传送门(内部题149) 输入格式 每个测试点第一行为四个正整数$n,b,s,m$,含义如题目所述. 接下来$m$行,每行三个非负整数$u,v,l$,表示从点$u$到点$v$有一条权值为$l$的有向 ...

随机推荐

  1. HTML-字符实体,平方米(㎡)m²

    转载自:https://blog.csdn.net/hangGe0111/article/details/80983250

  2. 【HANA系列】SAP HANA的特点总结

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[HANA系列]SAP HANA的特点总结   ...

  3. Caused by: java.lang.ClassNotFoundException: com.alibaba.dubbo.common.Version

    <dependency> <groupId>com.alibaba.boot</groupId> <artifactId>dubbo-spring-bo ...

  4. C++ std::isnan等函数的使用

    C和C++11标准提供了类似于isnan.isfinite.isinf.isnormal.fpclassify分别用于判断是非数(NaN)值.有限制.无穷值.正常数值等. 今天在使用Modbus读取设 ...

  5. RedHat可用的几处软件源

    rpmforge仓库 http://repoforge.org/use/ http://rpms.famillecollet.com/

  6. [暑假集训Day1T3]新的开始

    新建一个虚拟节点后直接跑最小生成树即可,从虚拟节点往每个节点连的边权为每个点建发电站的代价,许多人的考场贪心策略是:先构建原图的最小生成树后找一个花费最小的地方建发电厂.但是这样做不对的地方在于:如果 ...

  7. jquery+html实现前端的上传图片预览

        就是这样的一个功能,点击加号,出现图片选择,然后选择好以后生成预览. input那么丑,UI看不惯,一定要改成加号 我就用了fa的图标,外部套一个bootstrap4中的class:borde ...

  8. rdev - 查询/设置内核映像文件的根设备,RAM 磁盘大小或视频模式

    总览 SYNOPSIS rdev [ -rvh ] [ -o offset ] [ image [ value [ offset ] ] ] rdev [ -o offset ] [ image [ ...

  9. spring boot 不连接数据库启动

    Consider the following:    If you want an embedded database (H2, HSQL or Derby), please put it on th ...

  10. 02 | 日志系统:一条SQL更新语句是如何执行的? 学习记录

    <MySQL实战45讲>02 | 日志系统:一条SQL更新语句是如何执行的? 学习记录http://naotu.baidu.com/file/ad320c7a0e031c2d6db7b5a ...