[20190727NOIP模拟测试9]单(single) 题解(树上dp)
啊啊啊啊啊啊啊啊考场上差一点就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)的更多相关文章
- 洛谷【P2458】[SDOI2006]保安站岗 题解 树上DP
题目描述 五一来临,某地下超市为了便于疏通和指挥密集的人员和车辆,以免造成超市内的混乱和拥挤,准备临时从外单位调用部分保安来维持交通秩序. 已知整个地下超市的所有通道呈一棵树的形状:某些通道之间可以互 ...
- [CSP-S模拟测试]:Cicada与排序(概率DP)
题目传送门(内部题93) 输入格式 第一行一个整数$n$,代表数列的长度. 接下来一行$n$个数$a_i$,用空格分隔开. 输出格式 输出一行$n$个数,表示原数列上这个位置在执行后的期望位置,注意输 ...
- [CSP-S模拟测试]:点亮(状压DP+树上背包DP)
题目传送门(内部题121) 输入格式 第一行,一个正整数$n$. 第二行,$n-1$个正整数$p_2,p_3,...,p_n$.保证$p_u$是在$1$到$u-1$中等概率随机选取的. 接下来$n$行 ...
- [CSP-S模拟测试]:电压机制(图论+树上差分)
题目描述 科学家在“无限神机”($Infinity\ Machine$)找到一个奇怪的机制,这个机制有$N$个元件,有$M$条电线连接这些元件,所有元件都是连通的.两个元件之间可能有多条电线连接.科学 ...
- [CSP模拟测试43、44]题解
状态极差的两场.感觉现在自己的思维方式很是有问题. (但愿今天考试开始的一刻我不会看到H I J) A 考场上打了最短路+贪心,水了60. 然而正解其实比那30分贪心好想多了. 进行n次乘法后的结果一 ...
- [7.18NOIP模拟测试5]砍树 题解(数论分块)
题面(加密) 又考没学的姿势……不带这么玩的…… 考场上打了个模拟 骗到30分滚粗了 稍加思考(滑稽)可将题面转化为: 求一个最大的$d$,使得 $\sum \limits _{i=1}^n {(\l ...
- [NOIP模拟测试3] 建造游乐园 题解(欧拉图性质)
Orz 出题人石二队爷 我们可以先求出有n个点的联通欧拉图数量,然后使它删或增一条边得到我们要求的方案 也就是让它乘上$C_n^2$ (n个点里选2个点,要么删边要么连边,选择唯一) 那么接下来就是求 ...
- [NOIP模拟测试9]题(Problem) 题解 (组合数全家桶+dp)
达哥送分给我我都不要,感觉自己挺牛批. $type=0:$ 跟visit那题类似,枚举横向移动的步数直接推公式: $ans=\sum C_n^i \times C_i^{\frac{i}{2}} \t ...
- [CSP-S模拟测试]:任务分配(最短路+贪心+DP)
题目传送门(内部题149) 输入格式 每个测试点第一行为四个正整数$n,b,s,m$,含义如题目所述. 接下来$m$行,每行三个非负整数$u,v,l$,表示从点$u$到点$v$有一条权值为$l$的有向 ...
随机推荐
- Vagrant 手册之网络 - 端口转发
原文地址 Vagrantfile 配置文件中端口转发的网络标识符:forwarded_port,例如: config.vm.network "forwarded_port", gu ...
- 第 2 章 前端基础之CSS
一.CSS语法 CSS规则由两个主要的部分构成:选择器,以及一条或多条声明. ''' selector { property: value; property: value; ... property ...
- poj2010 Moo University - Financial Aid 优先队列
Description Bessie noted that although humans have many universities they can attend, cows have none ...
- php编程怎么和mysql连接
php连接mysql的方法: MySQLi - 面向对象 MySQLi - 面向过程 关闭连接 连接在脚本执行完后会自动关闭.你也可以使用以下代码来关闭连接: (MySQLi - 面向对象 MySQL ...
- python datetime模块的strftime()
strftime() 可以对datetime对象进行格式化,生成需要时间格式的时间 strptime() 可以对格式化后的时间再生成datetime对象 格式化时间时,如果不想要-来隔开,还可以 ...
- input限制小数点的位数
在做限制input小数点的时候,我本来想通过vue里面的的watch监听来实现, ---实现逻辑是,通过监听输入的内容,当出现"."(点)的时候,记录通过indexOf获取点的位置 ...
- P4158[SCOI2009]粉刷匠
题目描述 windy有 N 条木板需要被粉刷. 每条木板被分为 M 个格子. 每个格子要被刷成红色或蓝色. windy每次粉刷,只能选择一条木板上一段连续的格子,然后涂上一种颜色. 每个格子最多只能被 ...
- SpringBoot-技术专区-详细打印启动时异常堆栈信息
SpringBoot在项目启动时如果遇到异常并不能友好的打印出具体的堆栈错误信息,我们只能查看到简单的错误消息,以致于并不能及时解决发生的问题,针对这个问题SpringBoot提供了故障分析仪的概念( ...
- JavaScript代码内部执行顺序
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- 相对路径 分类: C# 2015-06-11 15:41 8人阅读 评论(0) 收藏
.绝对路径 绝对路径是指文件在硬盘上真正存在的路径.例如"bg.jpg"这个图片是存放在硬盘的"E:\book\网页布局代码\第2章"目录下,那么 &q ...