啊啊啊啊啊啊啊啊考场上差一点就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. 【NOIP2017】列队【可持久化线段树】

    题目链接 题目描述 Sylvia 是一个热爱学习的女孩子. 前段时间,Sylvia 参加了学校的军训.众所周知,军训的时候需要站方阵. Sylvia 所在的方阵中有n×mn×m名学生,方阵的行数为 n ...

  2. 有趣的linux指令

    1.cmatrix sudo apt-get update sudo apt-get install cmatrix 2.asciiquarium wget http://search.cpan.or ...

  3. selenium python 报错“ unable to find binary in default location”

    selenium python 报错如下: raise exception_class(message, screen, stacktrace)selenium.common.exceptions.W ...

  4. js 函数 写法

    // function ckeckName(){}; // function checkUser(){}; // function checkPassWorld(){}; // var checkNa ...

  5. 《JAVA设计模式》之抽象工厂模式(Abstract Factory)

    场景问题 举个生活中常见的例子——组装电脑,我们在组装电脑的时候,通常需要选择一系列的配件,比如CPU.硬盘.内存.主板.电源.机箱等.为讨论使用简单点,只考虑选择CPU和主板的问题. 事实上,在选择 ...

  6. 那些年很脑残的bugs

    1.老师给了前端界面,我们用java写后台. 我改了表单form的action属性,让它跳到自己写的servlet上面去.自己在servlet里面对数据库一顿操作猛如虎,然后让servlet跳回原来页 ...

  7. BZOJ 1040 [ZJOI2008]骑士 (基环树+树形DP)

    <题目链接> 题目大意: Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英.他们劫富济贫,惩恶扬善,受到社会各界的赞扬.最近发生了一件可怕的事情,邪恶的Y国发动了一场针对Z国的 ...

  8. 客户端GUI结构学习总结

    这几个月的开发工作主要是关于游戏内GUI的,业务开发之余也时常会看看客户端工程里的GUI系统这一块的代码,这里系统的总结下. 一.GUI树形结构 在GUI中所有的控件都遵循树形结构: 在客户端初始化时 ...

  9. [USACO06FEB]摊位预订Stall Reservations(贪心)

    [USACO06FEB]摊位预订Stall Reservations 题目描述 Oh those picky N (1 <= N <= 50,000) cows! They are so ...

  10. 【串线篇】SQL映射文件select简单查询标签

    一.参数(Parameters)传递 单个参数 基本类型:取值#{hahaha}随便写 多个参数 <!--   public Employee getEmpById(Integer id,Str ...