首页 > 其他分享 >洛谷 P5311 [Ynoi2011] 成都七中

洛谷 P5311 [Ynoi2011] 成都七中

时间:2023-12-29 22:12:26浏览次数:20  
标签:rt sz 七中 int Ynoi2011 dfn maxn res P5311

洛谷传送门

转化一下题意,变成求 \(x\) 在只经过编号 \(\in [l, r]\) 的点,能走到多少种颜色。

考虑建出点分树。一个结论是原树上的一个连通块,一定存在一个点,使得它在点分树上的子树完全包含这个连通块的所有点。证明考虑点分治的过程,一个连通块如果没被其中一个点剖开就一定在同一个子树。

所以对于一个询问 \((l, r, x)\),我们可以把 \(x\) 跳到点分树上最浅的与 \(x\) 在同一连通块的点 \(y\)。判断两个点是否在同一连通块只需求路径最小值和最大值,可以树剖 + ST 表解决。

这样问题变为子树内的问题了,即一次询问要求点分树上 \(y\) 的子树内,到 \(y\) 的路径最大值 \(L \ge l\),最小值 \(R \le r\) 的这些点中不同的颜色 \(c\) 数量。

因为点分树上 \(\sum sz_u = O(n \log n)\) 所以可以暴力遍历子树求出所有的 \((L, R, c)\)。套路地离线扫描线,扫右端点 \(r\),同时加入所有 \(R \le r\) 的点。设 \(b_x\) 为颜色 \(x\) 的左端点最大值。那么一种颜色 \(c\) 对于一个 \(l\) 当 \(b_c \ge l\) 时有 \(1\) 的贡献。树状数组维护即可。

时间复杂度 \(O(n \log^2 n + q \log n)\)。

code
// Problem: P5311 [Ynoi2011] 成都七中
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P5311
// Memory Limit: 250 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<int, int> pii;

const int maxn = 100100;
const int logn = 22;

int n, m, a[maxn], b[maxn], c[maxn], ans[maxn];
int A[logn][maxn], B[logn][maxn], fa[maxn], dep[maxn], son[maxn], top[maxn], dfn[maxn], tim;
vector<int> G[maxn];

int f[maxn], sz[maxn], rt;
bool vis[maxn];

struct node {
	int l, r, i;
	node(int a = 0, int b = 0, int c = 0) : l(a), r(b), i(c) {}
};
vector<node> qq[maxn], vc[maxn];

void dfs2(int u, int fa, int t) {
	sz[u] = 1;
	f[u] = 0;
	for (int v : G[u]) {
		if (v == fa || vis[v]) {
			continue;
		}
		dfs2(v, u, t);
		sz[u] += sz[v];
		f[u] = max(f[u], sz[v]);
	}
	f[u] = max(f[u], t - sz[u]);
	if (!rt || f[u] < f[rt]) {
		rt = u;
	}
}

void dfs3(int u, int fa, int l, int r, int rt) {
	vc[rt].pb(l, r, a[u]);
	for (int v : G[u]) {
		if (v == fa || vis[v]) {
			continue;
		}
		dfs3(v, u, min(l, v), max(r, v), rt);
	}
}

void dfs(int u) {
	vis[u] = 1;
	dfs3(u, -1, u, u, u);
	for (int v : G[u]) {
		if (vis[v]) {
			continue;
		}
		rt = 0;
		dfs2(v, u, sz[v]);
		dfs2(rt, -1, sz[v]);
		// printf("%d -> %d\n", rt, u);
		b[rt] = u;
		dfs(rt);
	}
}

int dfs4(int u, int f, int d) {
	fa[u] = f;
	sz[u] = 1;
	dep[u] = d;
	int mx = -1;
	for (int v : G[u]) {
		if (v == f) {
			continue;
		}
		sz[u] += dfs4(v, u, d + 1);
		if (sz[v] > mx) {
			son[u] = v;
			mx = sz[v];
		}
	}
	return sz[u];
}

void dfs5(int u, int tp) {
	top[u] = tp;
	dfn[u] = ++tim;
	A[0][tim] = B[0][tim] = u;
	if (!son[u]) {
		return;
	}
	dfs5(son[u], tp);
	for (int v : G[u]) {
		if (!dfn[v]) {
			dfs5(v, v);
		}
	}
}

inline int qmin(int l, int r) {
	int k = __lg(r - l + 1);
	return min(A[k][l], A[k][r - (1 << k) + 1]);
}

inline int qmax(int l, int r) {
	int k = __lg(r - l + 1);
	return max(B[k][l], B[k][r - (1 << k) + 1]);
}

inline int querymin(int x, int y) {
	int res = x;
	while (top[x] != top[y]) {
		if (dep[top[x]] < dep[top[y]]) {
			swap(x, y);
		}
		res = min(res, qmin(dfn[top[x]], dfn[x]));
		x = fa[top[x]];
	}
	if (dep[x] > dep[y]) {
		swap(x, y);
	}
	res = min(res, qmin(dfn[x], dfn[y]));
	return res;
}

inline int querymax(int x, int y) {
	int res = x;
	while (top[x] != top[y]) {
		if (dep[top[x]] < dep[top[y]]) {
			swap(x, y);
		}
		res = max(res, qmax(dfn[top[x]], dfn[x]));
		x = fa[top[x]];
	}
	if (dep[x] > dep[y]) {
		swap(x, y);
	}
	res = max(res, qmax(dfn[x], dfn[y]));
	return res;
}

namespace BIT {
	int c[maxn];
	
	inline void update(int x, int d) {
		for (int i = x; i; i -= (i & (-i))) {
			c[i] += d;
		}
	}
	
	inline int query(int x) {
		int res = 0;
		for (int i = x; i <= n; i += (i & (-i))) {
			res += c[i];
		}
		return res;
	}
	
	inline void clear(int x) {
		for (int i = x; i; i -= (i & (-i))) {
			c[i] = 0;
		}
	}
}

void solve() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++i) {
		scanf("%d", &a[i]);
	}
	for (int i = 1, u, v; i < n; ++i) {
		scanf("%d%d", &u, &v);
		G[u].pb(v);
		G[v].pb(u);
	}
	dfs2(1, -1, n);
	dfs2(rt, -1, n);
	dfs(rt);
	dfs4(1, -1, 1);
	dfs5(1, 1);
	for (int j = 1; (1 << j) <= n; ++j) {
		for (int i = 1; i + (1 << j) - 1 <= n; ++i) {
			A[j][i] = min(A[j - 1][i], A[j - 1][i + (1 << (j - 1))]);
			B[j][i] = max(B[j - 1][i], B[j - 1][i + (1 << (j - 1))]);
		}
	}
	for (int i = 1, l, r, x; i <= m; ++i) {
		scanf("%d%d%d", &l, &r, &x);
		int y = x;
		while (b[y]) {
			y = b[y];
			if (l <= querymin(x, y) && querymax(x, y) <= r) {
				x = y;
			}
		}
		// printf("%d %d %d %d\n", x, l, r, i);
		qq[x].pb(l, r, i);
	}
	for (int i = 1; i <= n; ++i) {
		if (qq[i].empty()) {
			continue;
		}
		sort(vc[i].begin(), vc[i].end(), [&](const node &a, const node &b) {
			return a.r < b.r;
		});
		sort(qq[i].begin(), qq[i].end(), [&](const node &a, const node &b) {
			return a.r < b.r;
		});
		int j = 0;
		for (node u : qq[i]) {
			while (j < (int)vc[i].size() && vc[i][j].r <= u.r) {
				if (c[vc[i][j].i]) {
					BIT::update(c[vc[i][j].i], -1);
				}
				c[vc[i][j].i] = max(c[vc[i][j].i], vc[i][j].l);
				BIT::update(c[vc[i][j].i], 1);
				++j;
			}
			ans[u.i] = BIT::query(u.l);
		}
		for (node u : vc[i]) {
			BIT::clear(u.l);
			c[u.i] = 0;
		}
	}
	for (int i = 1; i <= m; ++i) {
		printf("%d\n", ans[i]);
	}
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}

标签:rt,sz,七中,int,Ynoi2011,dfn,maxn,res,P5311
From: https://www.cnblogs.com/zltzlt-blog/p/17935762.html

相关文章

  • P5314 [Ynoi2011] ODT
    好题,牛牛的一个套路。先树剖一下,我们可以很简单的用树状数组维护每个点的真实值。对于每个点只维护所有轻儿子的信息,对于每次询问的时候暴力加入当前点,重儿子以及父亲的信息,查询第\(k\)大,再删除信息即可。考虑链修改的影响。因为只维护的是轻儿子的信息,那么只有链上的所有轻......
  • P5311 [Ynoi2011] 成都七中
    我永远喜欢数据结构。题目传送门给出\(n\)个点的树,点有颜色\(a_i\)。有\(q\)次询问,每次询问给出\(l,r,x\),求保留\([l,r]\)范围内的节点时,\(x\)所在联通块中有多少种本质不同的颜色。询问之间相互独立。不保留一个点的定义是,将这个点以及与其相邻的边全部删除。......
  • P5309 [Ynoi2011] 初始化
    题目传送门本来不想写这道\(shabi\)卡肠题的,但还是写了。分块+根号分治。考虑对\(x\)的大小分类讨论:若\(x>=\sqrt{n}\),很明显最多只会加\(\sqrt{n}\)次,暴力加即可,用分块维护每个块内的\(sum\),查询就直接散块加上整块即可。若\(x<\sqrt{n}\),考虑累加\(x,y\)相同的......
  • 「Ynoi2011」成都七中
    「Ynoi2011」成都七中题意:询问\(([l,r],x)\),表示将树中编号在\([l,r]\)内的所有节点保留,求\(x\)所在连通块中颜色种类数可以转化为从\(x\)出发且只经过节点范围在\([l,r]\)的路径上的颜色种类数,是路径问题且多次询问,所以可以考虑点分树但是可以发现,一个节点的答案可......
  • 成都七中 解题报告
    点分树有这么一个性质:你总能找到一个点,使得原树中这个点所在的连通块在这个点的子树内(如果整棵树没有被破坏,这个点就是根节点)。这个结论过于显然,证明只有一句话:点分树上的......
  • Ynoi2011题解
    d1t1初始化题意一个长度为\(n\)的序列,\(q\)次操作。询问\([l,r]\)的区间和。将所有\(\bmodx=y\)的下标位置加\(z\)。\(n,q\leq2\times10^5\)。题解......
  • P5309 [Ynoi2011] 初始化
    P5309[Ynoi2011]初始化考虑暴力,模拟题意,时间复杂度竟是\(O(\frac{n^2}{x})\),那么对于\(x\ge\sqrt{n}\)的修改就可以暴力了,这不是根号分治吗。再去考虑\(x<\sqrt{n}......
  • 从 Ynoi2011 初始化 看卡常
    一般情况下,程序运行消耗时间主要与时间复杂度有关,超时与否取决于算法是否正确。但对于某些题目,时间复杂度正确的程序也无法通过,这时我们就需要卡常数,即通过优化一些操作的......
  • [Ynoi2011] 成都七中
    linkSolution不是分块的Ynoi。/jk我们注意到树上一个连通块一定存在一个节点使得连通块里面所有节点都在它子树内。点分树同理。那么对于一次查询\((l,r,x)\),我们可以......
  • luogu P5311 [Ynoi2011] 成都七中
    题面传送门首先考虑暴力怎么做。按照UNRD2T2找到每个联通块最高点的套路,我们可以找到每个询问点的祖先中,这个点到祖先路径上的点全部位于\([l,r]\)区间中的最浅的祖先,那么......