[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$的有向 ...
随机推荐
- Linux 中设置进程通过 systemctl 启动
对于某些脚本或需要启动命令的程序,可以通过创建 xx.service 服务文件来使用 systemctl 控制. 例如,对于 docker-compose,其后台启动且忽略输出信息的命令为: $ no ...
- poj3126Prime Path (BFS+素数筛)
素数筛:需要一个数组进行标记 最小的素数2,所有是2的倍数的数都是合数,对合数进行标记,然后找大于2的第一个非标记的数(肯定是素数),将其倍数进行标记,如此反复,若是找n以内的所有素数,只需要对[2, ...
- Ubuntu安装byzanz截取动态效果图
byzanz-record主要参数选项 用法: byzanz-record [选项...] 录制您的当前桌面会话 帮助选项: -?, --help 显示帮助选项 --help-all 显示全部帮助选项 ...
- php abs函数怎么用?
php abs函数怎么用? abs()函数的作用是返回一个数的绝对值.语法是abs(number),如果参数 number 是 float,则返回的类型也是 float,否则返回 integer(因为 ...
- 同步GitHub上fork的项目
最近在做“Python练习册,每天一个小程序”,fork了项目并贡献自己写的代码,项目还有其他人在贡献代码,每天都会更新,这就涉及到了自己fork的项目与原项目的同步更新问题,下面就是我最常用的方法. ...
- Maven安装、配置环境变量
一.首先在官网下载安装maven 1.进入官网 2.找到下载位置 3.点进去后是最新版的,若需要最新版就下这个,需要旧版本接着往下滑 4.下载历史版本 (1)点击"archives" ...
- POJ 3764 The xor-longest Path (01字典树)
<题目链接> 题目大意: 给定一颗$n$个节点$(n\leq10^5)$,有边权的树,其边权$(0\leq w < 2^{31})$.让你求出这棵树上任意两个节点之间的异或最大值. ...
- this的指向问题 call apply bind 中的this
在函数中this指向谁: 函数中的this指向谁,是由函数被调用的那一刻就确定下来的 平时确定一个函数中的this是谁,我们需要通过调用模式来确定 1. 函数调用模式 this ---> ...
- 屏蔽命令任何输出的:>/dev/null 2>&1
- smbspool - 将一个打印文件发送到一台SMB打印机
总览 SYNOPSIS smbspool {job} {user} {title} {copies} {options} [filename] 描述 DESCRIPTION 此程序是Samba(7)套 ...