首页 > 其他分享 >P9437 『XYGOI round1』一棵树 题解

P9437 『XYGOI round1』一棵树 题解

时间:2023-08-04 23:22:42浏览次数:36  
标签:mylog fa P9437 题解 LL int XYGOI siz mod

赛时一眼换根 dp,然后调调调了大概 1h+。
题目传送门

什么是换根 dp

在大多数树形 dp 中,我们只考虑对根的贡献,而一部分题目需要算出对所有点的贡献,一个比较显然的做法是对每个点都跑一次树形 dp,但是大大增加了时间复杂度,是我们不能接受的。

树形 dp 中的换根 dp 问题又被称为二次扫描,通常不会指定根结点,并且根结点的变化会对一些值,例如子结点深度和、点权和等产生影响。

换根 dp 的常用套路钦定某一个点为根,然后从根开始进行两次 dfs,第一次记录自己子树内中的节点对自己的贡献,第二次记录不属于自己子树中的点对自己的贡献。

Solution

不妨钦定 1 为根。
考虑令 \(f_u = \sum\limits_{v \in subtree(u)} w(v, u)\),\(g_u = (\sum\limits_{v \notin subtree(u)} w(v, u)) + w_u\)。为什么要加上 \(w_u\)? 是为了方便转移(不加好像也不是很麻烦)。

首先我们要实现一个位数的函数。

LL mylog(LL v) {
	if(v == 0) return 10;
	int res = 0;
	while(v) res ++, v /= 10;
	return pow(10, res);
} 

计算 \(f\) 数组

考虑从儿子 \(v\) 转移到 \(u\),显然为 \(f_v \times mylog(w_u) + siz_v \times w_u\)。

得到 \(f_u = (\sum\limits_{v \in subtree(u)} f_v) \times mylog(w_u) + w_u \times siz_u\) 。

void dfs1(int u, int fa) {
	siz[u] = 1;
	for(auto v : e[u])
		if(v != fa)	{
			dfs1(v, u);
			siz[u] += siz[v];
			f[u] = (f[u] + f[v]) % mod;
		}
	f[u] = (f[u] * mylog(w[u]) % mod + w[u] * siz[u] % mod) % mod;
}

计算 \(g\) 数组

如何计算子树外的点对自己的贡献呢?
不妨先想想从根节点转移到第二层节点。

\(1\) 转移到 \(u\)。

先算出除 \(subtree(u)\) 外的点对 \(1\) 的贡献,显然为 \(f_1\) 减去 \(u\) 对 \(1\) 的贡献 \(f_u * mylog(w_1) + siz_u * w_1\)。

再考虑 \(u\) 对儿子 \(v\) 的贡献,为 \(f_u + g_u\) 减去 \(v\) 对 \(u\) 的贡献,再把这些继续用计算 \(f\) 的方法转移到 \(v\) 上去。

void dfs2(int u, int fa) {
	for(auto v : e[u])
		if(v != fa)	{
			g[v] = ((f[u] + g[u] - val(v, u) - w[u]) % mod * mylog(w[v]) % mod + (siz[1] - siz[v] + 1) % mod * w[v] % mod) % mod;
			dfs2(v, u);
		}
}
AC code
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6 + 5, INF = 0x3f3f3f3f;
const LL mod = 998244353;
int n, m;
LL w[N];
vector<int> e[N];
LL f[N], g[N], siz[N];
LL mylog(LL v) {
	if(v == 0) return 10;
	int res = 0;
	while(v) res ++, v /= 10;
	return pow(10, res);
} 
LL val(int u, int v) {
	return (f[u] * mylog(w[v]) + siz[u] * w[v]) % mod;
}
void dfs1(int u, int fa) {
	siz[u] = 1;
	for(auto v : e[u])
		if(v != fa)	{
			dfs1(v, u);
			siz[u] += siz[v];
			f[u] = (f[u] + f[v]) % mod;
		}
	f[u] = (f[u] * mylog(w[u]) % mod + w[u] * siz[u] % mod) % mod;
}
void dfs2(int u, int fa) {
	for(auto v : e[u])
		if(v != fa)	{
			g[v] = ((f[u] + g[u] - val(v, u) - w[u]) % mod * mylog(w[v]) % mod + (siz[1] - siz[v] + 1) % mod * w[v] % mod) % mod;
			dfs2(v, u);
		}
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin >> n;
    for(int i = 1; i <= n; i ++) cin >> w[i];
    for(int i = 2; i <= n; i ++) {
    	int p;
    	cin >> p;
    	e[p].push_back(i), e[i].push_back(p);
  	}
  	dfs1(1, 0);
  	g[1] = w[1];
  	dfs2(1, 0);
  	LL res = 0;
  	for(int i = 1; i <= n; i ++)
  		res = (res + f[i] + g[i] - w[i]) % mod;
  	cout << res << '\n';
    return 0;
}

标签:mylog,fa,P9437,题解,LL,int,XYGOI,siz,mod
From: https://www.cnblogs.com/Svemit/p/17607282.html

相关文章

  • 洛谷 P7911 [CSP-J 2021] 网络连接 题解
    写在前面一道普及级别的题目。CSP-J全国统一命题2021年第三题。本题解来自于一位真正的大佬。传送门https://www.luogu.com.cn/blog/xyf007/solution-p7911。题面信息来源于洛谷。请访问https://www.luogu.com.cn/problem/P7911。声明:本题解非商业用途,一切侵权行为请联系作......
  • We Were Both Childrent 题解
    将一个好理解的方法。题目说有n只青蛙,每只青蛙初始都在0位置,每秒会往前跳a_i。你可以在位置1到n设置一个陷阱,陷阱会抓住经过它的所有青蛙,求你最多能抓住多少青蛙。很简单,只要枚举质因数并判断是否合法即可。intn,ans=0;cin>>n;memset(cnt,0,sizeofcnt)......
  • Balanced Round 题解
    原题链接。题目大意给你一些数,问至少删掉多少数后两两不大于k。我们可以画图理解。最后我们取max(2,1),由于我们求的是合法的,所以还得用长度减去2,最终答案就是2。根据图我们就知道可以遍历一遍数组,用t记录下最长合法序列长度,最后用n-t即可。代码#include<bits/......
  • CF1491B Minimal Cost 题解
    调了两个多小时终于过了,交一发题解。题目分析如果你认真读题就会发现,这道题看似有很多种情况,但障碍的移动方式其实只有几种。如果当所有障碍物都在一列时,可以将某一个障碍水平移动一格,再垂直移动一格或者水平移动两格,那么答案就是v+min(u,v)。当有通路时,则无需移动,答案就是......
  • CF1682B AND Sorting 题解
    首先,我们按照题意,可以用0来作为中间的一个数来交换其他两个数,这种元素肯定是有的,那就是所有不在正确位置上的所有数的AND值,我们可以开一个数组a来模拟这个过程,a_i&a_j=X,那这里的X就起到我们的0的作用了。代码:#include<bits/stdc++.h>#defineintlonglongusin......
  • [NOI2021] 路径交点 题解
    [NOI2021]路径交点题解题意给定一张\(k\)层的有向图,第\(i\)层有\(n_i\)​个顶点,第​\(1\)层与第\(k\)​层顶点数相同。对于第​​\(j\)\((1\leqj<k)\)层的顶点,只会连向第\(j+1\)层的顶点。没有边连向第\(1\)层的顶点,第\(k\)层的顶点不会向其他顶点连边......
  • [Luogu P8744] 左孩子右兄弟 题解
    题目大意给定一棵节点个数为\(N\)的多叉树,求其通过"左孩子右兄弟"表示法转化成的二叉树,高度最高是多少。解决思路首先分辨出此题目是树状DP,并了解"左孩子右兄弟"表示法的转换方式,便开始考虑DP的状态转移方程。状态由于每个节点由\(1\)至\(N\)编号,那么就使用\(dp_{k......
  • 【题解】[2023牛客多校] Distance
    题目传送门:[2023牛客多校]Distance题意对于任意两个元素个数相同的set:A、B,每次可以执行以下两种操作之一:将A中的任意元素加一将B中的任意元素加一\(C(A,B)\)含义为将\(A、B\)改变为完全相同的set所需要花费的最小次数;初始给你两个set:\(S、T\),计算\(\sum_{A\subs......
  • P4795 [BalticOI 2018] 基因工程 题解
    题目传送门:Click。蒟蒻看见这道题,想了足足一个小时,过后顿有所悟,故作此篇。首先,看到题目,光是数据就已经达到了\(\operatorname{O}(nm)\)的级别,再看一看数据范围:\(3\leqn,m\leq4,100\)。显然是一道时间复杂度为\(\operatorname{O}(n,m)\)级别的题目。本蒟蒻首先观察了样......
  • 暑期竞赛培训 Day 16 <继续写题解>
    -[1][蓝桥杯2013省A]剪格子洛谷P8601题目描述如图\(1\)所示,\(3\times3\)的格子中填写了一些整数。我们沿着图中的红色线剪开,得到两个部分,每个部分的数字和都是\(60\)。本题的要求就是请你编程判定:对给定的\(m\timesn\)的格子中的整数,是否可以分割为两个部分,使......