首页 > 其他分享 >P3703 树点涂色 题解

P3703 树点涂色 题解

时间:2024-09-22 21:46:26浏览次数:9  
标签:dep P3703 题解 void son int add 涂色 Top

Statement

树,每个点有一个颜色,初始每个点颜色互不相同

  • 到根链涂上新颜色
  • 链颜色数
  • \(u\) 子树内点 \(v\) 到根路径的最多颜色数

Solution

首先,相同颜色的点一定构成一条从下往上的链

考虑 LCT,维护一个性质:一条实链的点的颜色相同.

于是 \(u\) 到根的颜色数 \(=\) \(u\) 到根的虚边数加一,记虚边数为 \(f(u)\).

于是第二个询问,等于 \(f(u)+f(v)-2\cdot f(lca)+1\)

第三个询问,等于 \(\max_{v\in\text{subtree}(u)}f(v)+1\)

如果问“\(u\) 子树内点 \(v\) 到 \(u\) 路径的最多颜色数”,答案等于 \(\max_{v\in\text{subtree}(u)}f(v)-f(u)+1\)

考虑修改操作,我们 access 一下

考虑线段树维护这个 \(f(u)\),操作有:区间加一 / 减一,区间问 \(\max\)

在 access 过程中进行区间加一 / 减一

注意到在 LCT 中设 \(u\) 的父亲为 \(v\),\((u,v)\) 由虚变实、\((w,v)\) 由实变虚,我们需要找到 \(u\) 所在实链的链顶、\(v\) 所在实链的第一个深度比 \(v\) 大的点

所以还要写个 \(\text{find}(u)\) 表示寻找 LCT 中 \(u\) 子树内深度最小的点

或者这样:多维护个信息 left[u] 表示 \(u\) 子树内最左边的点,这个可以在 push_up 时维护

Code

#include <bits/stdc++.h>
using namespace std;
#define rep(i, j, k) for (int i = (j); i <= (k); ++i)
#define reo(i, j, k) for (int i = (j); i >= (k); --i)
typedef long long ll;
const int N = 1e5 + 10, INF = 2e9;
vector<int> G[N];
int n, m;

int tim, dfn[N], revdfn[N], sz[N], dep[N], Fa[N], son[N], Top[N];
void DFS1(int u, int fa) {
	dep[u] = dep[fa] + 1, sz[u] = 1, Fa[u] = fa;
	for (int v : G[u]) 
		if (v != fa) {
			DFS1(v, u), sz[u] += sz[v];
			if (sz[v] > sz[son[u]]) {
				son[u] = v;
			}
		}
}
void DFS2(int u, int tp) {
	dfn[u] = ++tim, revdfn[tim] = u, Top[u] = tp;
	if (son[u]) DFS2(son[u], tp);
	for (int v : G[u])
		if (v != Fa[u] && v != son[u])
			DFS2(v, v);
}
int LCA(int u, int v) {
	while (Top[u] != Top[v]) {
		if (dep[Top[u]] < dep[Top[v]]) {
			v = Fa[Top[v]];
		} else {
			u = Fa[Top[u]];
		}
	}
	return dep[u] < dep[v] ? u : v;
}

namespace SegmentTree {
	int add[N << 2], mx[N << 2];
  #define lc (u << 1)
  #define rc ((u << 1) | 1)
  #define mid ((l + r) >> 1)
	void up(int u) {
		mx[u] = max(mx[lc], mx[rc]);
	}
	void Add(int u, int v) {
		add[u] += v, mx[u] += v;
	}
	void down(int u) {
		if (add[u]) Add(lc, add[u]), Add(rc, add[u]), add[u] = 0;
	}
	void Build(int u, int l, int r) {
		add[u] = 0;
		if (l == r) return (void)(mx[u] = dep[revdfn[l]] - 1);
		Build(lc, l, mid), Build(rc, mid + 1, r), up(u);
	}
	void Upd(int u, int l, int r, int x, int y, int v) {
		if (y < l || r < x || x > y) return;
		if (x <= l && r <= y) return Add(u, v);
		down(u), Upd(lc, l, mid, x, y, v), Upd(rc, mid + 1, r, x, y, v), up(u);
	}
	int Qry(int u, int l, int r, int x, int y) {
		if (y < l || r < x || x > y) return -INF;
		if (x <= l && r <= y) return mx[u];
		return down(u), max(Qry(lc, l, mid, x, y), Qry(rc, mid + 1, r, x, y));
	}
  #undef lc
  #undef rc
  #undef mid
	int Query(int x) {
		return Qry(1, 1, n, x, x);
	}
	int Query(int x, int y) {
		return Qry(1, 1, n, x, y);
	}
}

namespace LinkCutTree {
	int fa[N], ch[N][2], left[N];
  #define get(u) (u == ch[fa[u]][1])
  #define nrt(u) (u == ch[fa[u]][0] || u == ch[fa[u]][1])
	void up(int u) {
		if (ch[u][0]) left[u] = left[ch[u][0]];
		else left[u] = u;
	}
	void rot(int u) {
		int f = fa[u], g = fa[f], k = get(u);
		if (nrt(f)) ch[g][get(f)] = u;
		ch[f][k] = ch[u][!k];
		if (ch[u][!k]) fa[ch[u][!k]] = f;
		ch[u][!k] = f, fa[f] = u, fa[u] = g, up(f), up(u);
	}
	void splay(int u) {
		for (; nrt(u); rot(u)) if (nrt(fa[u])) rot(get(u) == get(fa[u]) ? fa[u] : u);
	}
	void access(int u) {
		using namespace SegmentTree;
		for (int v = 0; u; v = u, u = fa[u]) {
			splay(u);
			if (v) {
				int p = left[v];
				Upd(1, 1, n, dfn[p], dfn[p] + sz[p] - 1, -1);
			}
			if (ch[u][1]) {
				int p = left[ch[u][1]];
				Upd(1, 1, n, dfn[p], dfn[p] + sz[p] - 1, 1);
			}
			ch[u][1] = v, up(u);
		}
	}
  #undef get
  #undef nrt
}

int main() {
	using namespace LinkCutTree;
	using namespace SegmentTree;
	ios::sync_with_stdio(false), cin.tie(nullptr);
	cin >> n >> m;
	rep(i, 1, n - 1) {
		int u, v;
		cin >> u >> v;
		G[u].push_back(v), G[v].push_back(u);
	}
	DFS1(1, 0), DFS2(1, 1), Build(1, 1, n);
	rep(i, 1, n) fa[i] = Fa[i];
	while (m--) {
		int op, x, y;
		cin >> op;
		if (op == 1) {
			cin >> x, access(x);
		}
		if (op == 2) {
			cin >> x >> y;
			cout << Query(dfn[x]) + Query(dfn[y]) - 2 * Query(dfn[LCA(x, y)]) + 1 << '\n';
		}
		if (op == 3) {
			cin >> x;
			cout << Query(dfn[x], dfn[x] + sz[x] - 1) + 1 << '\n';
		}
	}
	return 0;
}

标签:dep,P3703,题解,void,son,int,add,涂色,Top
From: https://www.cnblogs.com/laijinyi/p/18425956

相关文章

  • 最长公共子串 题解
    StatementQ7.1.2.4,时限4s给一个串,定义\(\mathrm{LCS}\)为最长公共子串长度,\(q\)次询问,每次给出\(l,r\),求\[\operatorname{xor}_{i=1}^{r-l+1}\{i+\mathrm{LCS}(S[l,l+i-1],S[l+i-1,r])\}\]\(n\le10^5,q\le30\)Solutiontag:SA,线段树维护分治结构,orzhunction题目就是......
  • BZOJ 2555 = P5212 SubString 题解
    Statement给你一个字符串 \(\text{init}\),要求你支持两个操作:在当前字符串的后面插入一个字符串;询问字符串 \(s\) 在当前字符串中出现了几次?(作为连续子串)你必须在线支持这些操作。Solutionextend中link[cur]=q,相当于link,链加新建copy那里,相当于link,cut,链加询......
  • 2024 秋季模拟赛题解
    2024秋季模拟赛题解CSP-S模拟赛2024.9.8CSP-S模拟赛28T1签到题。对\(b\)分解质因数后便容易求解。T2考虑枚举\(\gcd(S)\)的取值\(x\),则\(\operatorname{lcm}(S)=m-x\)。那么同时变形\(\gcd\)和\(\operatorname{lcm}\)变为\(\gcd(S)=1,\operatorname{lcm}......
  • BZOJ 3277 串 题解
    Statement给 \(n\) 个串,问每个串有多少子串是所有 \(n\) 个串中至少 \(k\) 个串的子串。Solution1%%注意到\(\text{LCP}\)在后缀排序后一定是连续的一段,包含一个串的区间是连续的先预处理出对于所有左端点\(l\),最左的\(r\)满足\([l..r]\)中出现了至少\(k\)个......
  • BZOJ 4310 跳蚤 题解
    Statement把\(S\)分成不超过\(k\)段,使每段的最大子串中的最大串最小。输出这个串。Solution按排名二分这个串,check中从右往左贪心地划分,需要实现\(O(1)\)比较两个子串大小。#include<bits/stdc++.h>usingnamespacestd;#definerep(i,j,k)for(inti=(j);i<......
  • BZOJ 4545 DQS 的 trie 题解
    Statement维护一棵树,边权\(\in\{\texttta,\textttb,\textttc\}\),根为\(1\),定义这棵树的子串为从\(1\)走到所有点构成的字符串的所有后缀,需要支持以下操作:问当前树的本质不同子串数给一个点添加一棵子树问一个串在当前树中作为子串的出现次数Solution直接广义SAM+......
  • 力扣72-编辑距离(Java详细题解)
    题目链接:力扣72-编辑距离前情提要:因为本人最近都来刷dp类的题目所以该题就默认用dp方法来做。dp五部曲。1.确定dp数组和i下标的含义。2.确定递推公式。3.dp初始化。4.确定dp的遍历顺序。5.如果没有ac打印dp数组利于debug。每一个dp题目如果都用这五步分析清楚,那么......
  • ABC372 F 题解
    F-TeleportingTakahashi2先把问题转化一下:把环断开成链,复制\((K+1)\)层,每走一步就相当于前进一层:可以想到一个简单的dp:设\(f(i,j)\)表示走到第\(i\)层第\(j\)个位置的方案数。初始化:\(f(0,1)=1\),其它均为\(0\),表示Takahashi从第\(0\)层的\(1\)位置......
  • 【题解】「Public NOIP Round #2」找零
    【题解】「PublicNOIPRound#2」找零[官解](PublicNOIPRound#2题解-博客-Qingyu的博客(pjudge.ac))Tag:背包、dp凸优化决策点单调触碰到知识点盲区了,所以来写几笔。首先,由于我们只关心最终状态下\(1\)的最多个数,其实有用的面值只有\(5,1\)(其她的可以当成若干......
  • U389139 至少有一位重复的数字-题解
    题目传送门一、举例说明以\(654923\)为例要判断在\([0,654923]\)区间至少有一位重复的数值的数,可以考虑其补集,即\(\color{red}所有位数均不重复的数\),用\(N\)减去补集即为结果。首先可以将其分为两种情况。情况一,位数小于\(6\)位。所有位数均不重复的有\(9\time......