首页 > 其他分享 >CF708C Centroids

CF708C Centroids

时间:2024-08-08 11:32:31浏览次数:6  
标签:sz CF708C int Centroids part1 part2 子树 maxch

题意

来自洛谷:

image

思路

记录每个点 \(u\) 所在子树可以删去的最大的部分 \(part1\) 和次大的部分 \(part2\) 和除了 \(u\) 的子树以外的部分可以删去的最大的部分 \(up\),这些部分必须要求小于等于 \(\dfrac{n}{2}\),和找树的中心(注意不是重心)的思路差不多。

注意:\(part1, part2\) 不能同时更新,\(up\) 的更新要注意点的取舍。

然后记录一下结点 \(u\) 子树最大的结点 \(maxch[u]\)。


对于每个点,如果他本来就是重心那它不用变。

否则其子树或者子树以外的部分必有大于 \(\dfrac{n}{2}\) 的部分。

子树大于 \(\dfrac{n}{2}\),如果 \(sz[maxch[u]] - part1[maxch[u]] \le \dfrac{n}{2}\),说明可以割掉这一部分再接到 \(u\) 上去,这样任何一个部分也不会超过 \(\dfrac{n}{2}\)。

对于子树之外的一部分超过 \(\dfrac{n}{2}\),那就看如果 \((n - sz[u] - up[u]) \le \dfrac{n}{2}\),那说明满足条件,和上面类似。

代码

细节挺多,本来想着口胡一下就过了算了。

// Problem: Centroids
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/CF708C
// Memory Limit: 500 MB
// Time Limit: 4000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// up, part1, part2;

#include <bits/stdc++.h>

using namespace std;

const int N = 400010, M = 800010;

struct edge {
	int to, next;
} e[M];

int head[N], idx = 1;

void add(int u, int v) {
	idx++, e[idx].to = v, e[idx].next = head[u], head[u] = idx;
}

int n;
int sz[N];
int maxch[N], maxchr[N];
int part1[N], part2[N], up[N];          // part1: 最大的小于等于 n / 2 的子树,part2: 第二大的小于等于 n / 2 的子树
int chver[N];                           // 记录 part1 对应的结点
int f[N];
bool res[N];

bool updatepart(int u, int x, int c) {  // 更新 part1, part2
	if (x * 2 > n) return false;
	bool flag = false;
	if (part1[u] < x) {
		part2[u] = part1[u];
		part1[u] = x;
		chver[u] = c;
		flag = true;
	}
	else if (part2[u] < x) {
		part2[u] = x;
		flag = true;
	}
	return flag;
}

void dfs(int u, int fa) {               // dfs 记录子树大小,并找到 part1, part2 及 chver
	sz[u] = 1; f[u] = fa;
	for (int i = head[u]; i; i = e[i].next) {
		int to = e[i].to;
		if (to == fa) continue;
		dfs(to, u);
		sz[u] += sz[to];
		if (sz[to] > maxch[u]) {        // 记录 u 的最大子树
			maxch[u] = sz[to];
			maxchr[u] = to;
		}
		if (!updatepart(u, sz[to], to)) updatepart(u, part1[to], chver[to]);// 两者不能同时更新
	}
}

void dfsup(int u, int fa) {
	for (int i = head[u]; i; i = e[i].next) {       // 记录每个点除了这个子树的部分的 part1
		int to = e[i].to;
		if (to == fa) continue;
		up[to] = up[u];
		if ((n - sz[u]) * 2 <= n) up[to] = max(up[to], n - sz[u]);
		if (chver[u] == chver[to] || chver[u] == to) up[to] = max(up[to], part2[u]);
		else up[to] = max(up[to], part1[u]);
		dfsup(to, u);
	}
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	cin >> n;
	for (int i = 1; i < n; i++) {
		int u, v;
		cin >> u >> v;
		add(u, v), add(v, u);
	}
	dfs(1, 0);
	dfsup(1, 0);	
			
	for (int u = 1; u <= n; u++) {
		int mxs = max(maxch[u], n - sz[u]);
		if (mxs * 2 <= n) {
			res[u] = true;
			continue;
		}
		if (maxch[u] * 2 > n) {                             // u 的子树的最大部分大于 n / 2
			if ((maxch[u] - part1[maxchr[u]]) * 2 <= n) {   // u 的子树的最大的部分减去能减去的最大部分
				res[u] = true;
			}
		}
		else {
			if ((n - sz[u] - up[u]) * 2 <= n) {             // u 的子树之外最大部分大于 n / 2
				res[u] = true;                              // u 的子树之外的部分减去能减去的最大部分
			}
		}
	}
	for (int i = 1; i <= n; i++) cout << res[i] << ' ';
	return 0;
}

标签:sz,CF708C,int,Centroids,part1,part2,子树,maxch
From: https://www.cnblogs.com/Yuan-Jiawei/p/18348502

相关文章

  • CF1406C Link Cut Centroids
    思路如果一棵树有两个重心,那么从一个重心的一边切割一个点到另外一个重心即可。如果一棵树只有一个重心,那么随意断掉一个点再恢复即可。代码#include<bits/stdc++.h>usingnamespacestd;constintN=100010;structedge{ intto,next;}e[N*2];inthead[N]......
  • 【Centroids】不一样的解法
    前言一道好题,也就花了我一个下午而已。本人做法比较清奇,可以当做开阔思路参考,并不太建议实操(太难调了!)。文章较啰嗦,谅解。思路众所周知,我并不太喜推式子,所以我们考虑直接根据题目限定的条件硬刚。首先,我们先假定\(1\)为根,并求出每个点的子树的大小,记为数组\(size\),并且再......
  • CF708C Centroids
    对于一个不是重心的点\(u\),它必定有一棵子树\(T\)包含所有重心(如果有两个重心则它们必定相邻),显然\(|T|>\lfloor\frac{n}{2}\rfloor\),这阻碍了它成为重心。贪心地想,我们要在\(T\)中找出一棵子树\(S\)使得\(|S|\leq\lfloor\frac{n}{2}\rfloor\)且\(|S|\)尽可能大,然后将......
  • Two Centroids
    TwoCentroids先考虑对于一棵树,至少要加多少个点才能有两个重心。以重心为根,设最大儿子的子树大小为\(mx\),那么答案就为\((n-mx)-mx=n-2mx\)。接下来考虑如何在加点时维护最大子树,一个显然的性质,加一个点重心最多偏移一位,如果重心偏移,那么\(mx=\lfloor\frac{n}{2......
  • CF708C Centroids 换根dp
    CF708CCentroids一道换根DP。我们可以先找出树的一个重心,那么对于其他所有不是重心的点,它不能成为重心时因为它父亲的那一支节点数大于一半,而可以改造成功,则意味着可以在他父亲那一支里,可以找到子树u,使$siz[u]\len/2$&&$siz[fa]-siz[u]\len/2$。简而言之,就是对于每个......
  • AIM Tech Round 3 (Div. 1)-C. Centroids
    原题链接C.CentroidstimelimitpertestmemorylimitpertestinputoutputTree isaconnectedacyclicgraph.Supposeyouaregivenatreeconsistingof n vertices.Thevertexofthistreeiscalled centroid......
  • CodeForces 1827 D Two Centroids
    洛谷传送门CF传送门考虑固定一个重心,设\(k\)为重心最大子树大小,答案为\(n-2k\)。构造方法是往最大的子树塞叶子。树的重心有一个很好的性质,就是加一个叶子,重心最多移动一条边的距离。简单证一下,设重心为\(x\),往儿子\(u\)的子树中加叶子。如果\(sz_u>\left\lfloor......
  • CF708C Centroids(换根dp)
    题意:给定一颗树,你有一次将树改造的机会,改造的意思是删去一条边,再加入一条边,保证改造后还是一棵树。请问有多少点可以通过改造,成为这颗树的重心?(如果以某个点为根,每个子树的大小都不大于\(\dfrac{n}{2}\),则称某个点为重心)思路:是今天遇到的一道有意思的换根dp呃呃。从题意来看......
  • CodeForces - 708C Centroids
    题意:给出一棵有n个结点的树,对于每一个结点,如果任意删除一条边后再任意添加一条边能使这个结点成为这棵树的重心,则输出1;反之输出0。解:重心的特点:以重心为根节点时,其最大子......