啊啊啊啊啊啊啊啊考场上差一点就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. PHP Yii框架中使用smarty模板

    第一种方法 按照YII系统的办法生成视图觉得有点麻烦,觉得用smarty更省事.尝试着把smarty模板加进来了. date_default_timezone_set("PRC") ...

  2. Git/SVN相关

    svn配置不当: svn1.6之前的版本,通过访问.svn/.entries metasploit auxiliary/scanner/http/svn_scanner 在svn1.7之后的版本,通过 ...

  3. Win7崩溃程序目录

    很烦,占用系统空间,毫无用处 C:\Users\你的用户名\AppData\Local\CrashDumps C:\Users\你的用户名\AppData\Local\Microsoft\Window ...

  4. $_POST,$_GET,$_REQUEST区分

    PHP $_REQUEST PHP $_REQUEST 用于收集 HTML 表单提交的数据. 下面的例子展示了一个包含输入字段及提交按钮的表单.当用户通过点击提交按钮来提交表单数据时, 表单数据将发送 ...

  5. Oracle-随笔笔记

    1.重命名数据库表.重命名字段 alter table tablename1 rename to tablename2; alter table tablename1 rename column co ...

  6. 《剑指offer》面试题3 二维数组中的查找 Java版

    (二维数组,每行递增,每列递增.输入二维数组和一个整数,判断数组中是否含有此数.) 我的方法:拿到题目,根据题目条件我提取出这样一个特性:一个数的右边和下面的数都比它大.于是就可以写出一种递归的方法: ...

  7. daily plan -- 2019/5/20

    1.课内作业:物联网工程导论论文. 2.实验项目计划:学习Kinect彩色帧读取. 3.算法:LeetCode 动态规划一题. 4.英语:听力30分钟训练,英语单词. 今日心情: 进度反馈:计划基本完 ...

  8. web调试代理工具Whistle

    由于最近在学习微信小程序开发,项目中用到了https代理请求,所以用到了基于Node实现的跨平台web调试代理工具Whistle,在此做一记录. 完成https代理请求总共需要5个步骤. 一.安装No ...

  9. 【转】sed命令的基本操作

    原文链接 sed命令行格式为:          sed [-option]  ‘command’  输入文本/文件         常用选项:        -n∶取消默认的输出,使用安静(sile ...

  10. 全文搜索引擎 elasticsearch.net

    原文:https://www.cnblogs.com/lonelyxmas/p/10767436.html