首页 > 其他分享 >「CF1017G」The Tree

「CF1017G」The Tree

时间:2024-04-30 20:44:19浏览次数:26  
标签:CF1017G Node return int tr Tree mid 节点

这是一道非常 NB 的题意转化题.

Introduction

给定一棵树, 维护以下3个操作:

  1. 1 x 表示如果节点 \(x\) 为白色, 则将其染黑. 否则对这个节点的所有儿子递归进行相同操作;
  2. 2 x 表示将以节点 \(x\) 为 \(root\) 的子树染白;
  3. 3 x 表示查询节点 \(x\) 的颜色.

Striking Ideas

修改复杂度远远高于询问复杂度. 考虑平衡操作.

先不考虑操作二.
考虑将对儿子递归转化成对该节点进行信息修改, 可以发现如果考虑对一个已染色的节点再次染色, 相当于它被染色了 \(2\) 次, 也就等价于它的儿子节点都被染色.
为此, 我们发现, 如果将染黑一个节点表示为将该节点的权值 \(+1\), 则有如果一个节点是黑色, 则一定存在一个祖先节点 (可以是它本身), 使得路径上的权值之和大于等于距离. 为了方便做题, 我们将初始权值设置为 \(-1\), 则只需要最大后缀和 \(\geq 0\) 即可.

现在考虑操作二.
如果让子树染白, 即使子树内所有的节点的最大后缀和为 \(-1\), 可以先直接把子树内节点权值赋为 \(-1\), 再给子树根节点 \(u\) 的权值减少 \(v\), 使得 \(u\) 的最大后缀和为 \(-1\), 容易得到 \(v = query(u) + 1\), 其中 \(query(u)\) 表示最大后缀和.

Algorithm

直接树链剖分, 再用线段树维护最大后缀和即可. 时间复杂度 \(\mathcal O(n\log ^2n)\).

Code

#include <bits/stdc++.h>
using namespace std;

using ci = const int;

using i64 = long long;
using u64 = unsigned i64;
using u32 = unsigned;

const int N = 1e5 + 5;
const int inf = 0x3f3f3f3f;

int n, m;
vector<int> e[N];

int fa[N], siz[N], dep[N], son[N], top[N], dfn[N], rnk[N], tcnt;

void dfs1(int u) {
	siz[u] = 1;
	for (ci &v : e[u]) {
		if (v == fa[u]) continue;
		fa[v] = u;
		dep[v] = dep[u] + 1;
		dfs1(v);
		siz[u] += siz[v];
		if (siz[v] > siz[son[u]])
			son[u] = v;
	}
}

void dfs2(int u) {
	rnk[dfn[u] = ++tcnt] = u;
	if (son[fa[u]] != u) top[u] = u;
	else top[u] = top[fa[u]];

	if (son[u]) dfs2(son[u]);
	for (ci &v : e[u]) if (!dfn[v]) dfs2(v);
}

struct Node {
	int v, s;
	bool la;
	Node() {}
	Node(int _v, int _s, bool _la) : v(_v), s(_s), la(_la) {}

	inline Node operator+(const Node &o) const {
		return Node(max(v + o.s, o.v), s + o.s, 0);
	}
	inline void operator+=(const Node &o) {
		*this = *this + o;
	}
} tr[N << 2];

#define L (p << 1)
#define R (p << 1 | 1)

void pushdown(int p, int l, int r) {
	if (!tr[p].la) return;
	int mid = l + r >> 1;
	tr[L] = Node(-1, -(mid - l + 1), 1);
	tr[R] = Node(-1, -(r - mid), 1);
	tr[p].la = 0;
}

void build(int p, int l, int r) {
	if (l == r) {
		tr[p] = Node(-1, -1, 0);
		return;
	}

	int mid = l + r >> 1;
	build(L, l, mid);
	build(R, mid + 1, r);
	tr[p] = tr[L] + tr[R];
}

void cover(int p, int l, int r, ci &al, ci &ar) {
	if (al <= l && ar >= r) {
		tr[p] = Node(-1, -(r - l + 1), 1);
		return;
	}	
	pushdown(p, l, r);

	int mid = l + r >> 1;
	if (al <= mid) cover(L, l, mid, al, ar);
	if (ar > mid) cover(R, mid + 1, r, al, ar);
	tr[p] = tr[L] + tr[R];
}

void add(int p, int l, int r, ci &x, ci &y) {
	if (l == r) {
		tr[p].v += y;
		tr[p].s += y;
		return;
	}
	pushdown(p, l, r);

	int mid = l + r >> 1;
	if (x <= mid) add(L, l, mid, x, y);
	else add(R, mid + 1, r, x, y);
	tr[p] = tr[L] + tr[R];
}

Node query(int p, int l, int r, ci &al, ci &ar) {
	if (al <= l && ar >= r) return tr[p];
	pushdown(p, l, r);

	int mid = l + r >> 1;
	Node num(-inf, 0, 0);
	if (al <= mid) num += query(L, l, mid, al, ar);
	if (ar > mid) num += query(R, mid + 1, r, al, ar);
	return num;
}

#undef L
#undef R

inline int ask(int u) {
	Node num(-inf, 0, 0);
	while (u) {
		num = query(1, 1, n, dfn[top[u]], dfn[u]) + num;
		u = fa[top[u]];
	}
	return num.v;
}

int main() {
#ifndef ONLINE_JUDGE
	freopen(".in", "r", stdin);
	freopen(".out", "w", stdout);
#endif

	scanf("%d %d", &n, &m);
	for (int i = 2, x; i <= n; ++i) {
		scanf("%d", &x);
		e[x].push_back(i);
	}

	dfs1(1);
	dfs2(1);

	build(1, 1, n);

	while (m--) {
		static int op, x;
		scanf("%d %d", &op, &x);

		switch (op) {
			case 1: add(1, 1, n, dfn[x], 1); break;
			case 2:
				cover(1, 1, n, dfn[x], dfn[x] + siz[x] - 1);
				add(1, 1, n, dfn[x], -ask(x) - 1); break;
			case 3: puts(ask(x) >= 0 ? "black" : "white"); break;
		}
	}

	return 0;
}

标签:CF1017G,Node,return,int,tr,Tree,mid,节点
From: https://www.cnblogs.com/cqbzljh/p/18168650

相关文章

  • CF1951I Growing Trees
    MyBlogsCF1951IGrowingTrees首先考虑确定了\(x_i\)如何判定是否合法。可以很容易的找出这样一个必要条件:\(\foralli,x_i\leqk\)。这是两个点的情况,考虑点数更多的情况。手玩之后可以推广到:对于任意导出子图,要求其内部的边数\(\leqk(|S|-1)\)。这个条件也是充分的,证明......
  • 树(tree) [一]
    树(tree)[一]基本概念:​ 日常生活中,很多数据的组织形式本质上是一棵树。比如一个公司中的职员层级关系、一个学校中的院系层级关系、淘汰赛中的各次比赛队伍、一个家族中的族谱成员关系等都是树状逻辑结构。由于树状结构表现出来都是具有层次的,因此也被称为层次结构。树是一种......
  • JDK源码分析-TreeSet
    概述TreeSet是Java集合框架中用于存储唯一元素的树形数据结构,它实现了NavigableSet接口,这意味着TreeSet中的元素不仅是有序的,还支持一系列的导航方法。TreeSet的内部实现主要依赖于TreeMap,通过TreeMap的键来维护元素的排序。 类图从以上类图可以看到,TreeSet实现了三个接口,......
  • D. Distance in Tree
    https://codeforces.com/problemset/problem/161/D题意:给一棵树,求树上距离为k的节点对的数量。思路:树上dp,随便找个节点开始遍历。然后枚举当前点的距离为i的节点数与当前点的孩子节点距离为k-i-1的节点数相乘。总结:想到了树上dp,也想到了这个思路,但是没想到相乘跟开始遍历的......
  • Tree-shaking ESModule
     一、需求背景与收益Tree-shaking剪裁无用js与css,目前在dc组实现,首页效果如下:1、原文件5.19M,优化后2.61M2、gzip文件988.25KB, 优化后665.63KB3、Js文件减少三分之一,项目越久收益越高4、运行速度和用户体验都会提升5、Lighthouse性能评分提升大概4-8分6、属于攻坚技术......
  • ant-design Tree树形控件,通过expandedKeys控制收缩或折叠失效
    一、概述AntDesign的树形组件Tree,通过属性expandedKeys手动控制组件的展开和收缩时,点击节点后更新expandedKeys属性值可以正常展开,再点击左侧三角形小图标时(onExpand)却不能收缩了。  二、问题分析a.根据以往经验,出现keys的问题,一般是由key的数据重复或类型(尤其Number......
  • LSM Tree 简笔
    LSMTree总览写流程:就地写,写入memTable当activememTable满后,转变为readOnlymemTable,在合适时机flush入磁盘。当前level数据满后,进行归并操作,把数据排序,去重后转入下一level。背景介绍两种场景,读多写少,写多读少。原地写写操作:找到老数据所在位置,更新,IO操作,慢。......
  • vue3使用echarts的tree,自己写事件进行分页
    vue3使用echarts的tree,自己写事件进行分页  先到npmjs官网查看当前使用最多的版本https://www.npmjs.com/package/echarts 看了下5.5.0用的最多[email protected] 以下的demo(“@/flare”是后面的flare.json数据)<template><divid="chart-container"></div......
  • CF1709E XOR Tree
    linkSolution:PART1:转化首先套路地预处理出每个节点到根节点(\(1\)号节点)路径上的点权异或和\(w[u]\)。可以发现题意容易转化为:给定一棵\(n\)个节点的树,问你最少可以把它分成多少个联通块,使得每个连通块中的节点两两路径上的异或和不为0。易知对于一个节点,若它要被割......
  • CF771C Bear and Tree Jumps
    题目大意:给定一棵有\(n\)个节点的树,要你统计\(\sum_{1\lex\ley\len}{dist(x,y)/k}\)(\(dist(x,y)\)表示\(x\)到\(y\)的距离)\(n\le2\times10^5,k\le5\)解法:一道换根\(dp\)套路题。首先看到树上统计问题,考虑树形\(dp\),那么我们设\(g(u)\)为以\(......