ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

CF1187E Tree Painting

2022-03-26 10:33:52  阅读:189  来源: 互联网

标签:结点 白色 int siz Tree CF1187E 为根 include Painting


前言

您完全可以略过这段。

校内模拟赛的时候一位大奆佬搬了这题。

然后我切了。

个人认为这题是一道不错的换根dp,虽然略显套路。

题意简述

给定一棵有 \(n\) 个结点的无根树,所有结点都是白色的。

第一次操作可以随意使一个结点染成黑色,之后每次操作可以使一个与黑色结点相邻的白色结点变成黑色。

每次操作可以获得的权值为被染成黑色的白色结点所在的白色连通块的结点数量。

求可以获得的最大权值。

感觉自己写的好绕。

题目分析与解答

不难发现只要选定了第一个被染色的结点,答案也就确定了。

所以有一个朴素做法就是以枚举每个结点为根,都做一次树形dp。

以某一结点为根,记 \(f_i\) 表示以 \(i\) 为根的子树中,首先把 \(i\) 染成黑色的答案。

方程就是 \(f_u = siz_u+\sum\limits_{v \in son_u} f_v\)。

其中 \(siz_u\) 表示以 \(u\) 为根的子树大小,也就是染色时的白色连通块大小。

这部分比较好理解就不画图举例啦。

时间复杂度 \(O(n^2)\) ,稳稳地暴毙。

然后就会自然而然地想到换根dp。

先考虑以任意一点为根,不妨记为 \(rt\) ,求出 \(f\) 数组。

然后记 \(g_i\) 表示以 \(i\) 结点为根时的答案。

显然有 \(g_{rt} = f_{rt}\)。

然后考虑 \(g\) 数组从父亲到儿子的转移。

以样例为例:

我们假设当前以 \(1\) 号为根,求出了 \(f\) 数组,也就是知道了 \(g_1\) ,然后要求出 \(g_2\) 。

考虑一下答案的组成。

首先考虑 \(2\) 号结点的孩子的贡献,也就是图中蓝圈内的部分。这部分显然不会改变,贡献就是 \(f_2-siz_2\) 。

然后考虑父亲方向,也就是图中红圈部分对 \(g_2\) 的贡献。

那么除了以 \(2\) 号结点,与 \(1\) 一号结点相邻的其他子树都会对答案产生贡献,也就是说,我们只需要用以 \(1\) 号结点为根时的权值减去以 \(2\) 为根的子树的贡献即可,也就是 \(g_1-f_2-siz_2\) 。

不要忘了加上 \(n\) ,也就是初始的白色连通块大小。

综合一下上述两种方向的贡献,可以得到:\(g_2=(f_2-siz_2)+(g_1-f_2-siz_2)+n = g_1 + n - siz_2 \times 2\) 。

推广到所有结点,就可以得到:

\(g_u = g_{father_u} + n - siz_u \times 2\)

然后跑两遍 dfs 就愉快的解决啦。

Code:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>

using namespace std;

typedef long long ll;
const int MAXN = 200010;

int n;
struct edge{
	int ne, to;
	edge(int N=0, int T=0):ne(N),to(T){}
}e[MAXN<<1];
int fir[MAXN], num = 0;
inline void join(int a, int b)
{
	e[++num] = edge(fir[a], b);
	fir[a] = num;
}

ll siz[MAXN], f[MAXN], g[MAXN], ans = 0;

void dfs1(int u, int fa)
{
	siz[u] = 1;
	for(int i=fir[u];i;i=e[i].ne)
	{
		int v = e[i].to;
		if(v == fa) continue;
		dfs1(v, u);
		siz[u] += siz[v];
		f[u] += f[v];
	}
	f[u] += siz[u];
}
void dfs2(int u, int fa)
{
	for(int i=fir[u];i;i=e[i].ne)
	{
		int v = e[i].to;
		if(v == fa) continue;
		g[v] = n - siz[v] + g[u] - siz[v];
		dfs2(v, u);
	}
}

int main()
{
//	freopen("stars3.in","r",stdin);
//	freopen("stars.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<n;i++)
	{
		int a, b;
		scanf("%d%d",&a,&b);
		join(a, b);
		join(b, a);
	}
	dfs1(1, 0);	
	g[1] = f[1];
	dfs2(1, 0);
	ll ans = 0;
	for(int i=1;i<=n;i++)
		ans = max(ans, g[i]);
	printf("%lld\n",ans);
	return 0;
}

似乎只有我时而大括号换行,时而不换行?!

标签:结点,白色,int,siz,Tree,CF1187E,为根,include,Painting
来源: https://www.cnblogs.com/defK/p/16057861.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有