牛客练习赛42E 热爆了(Lct+树状数组)

牛客练习赛42E 热爆了(Lct+树状数组)

解题思路

小 X 决定出一道送温暖题来和大家一起愉悦
他给了你一棵 n 个节点的树,每个点有个点权
ai
现在他给了你 Q 个询问,每次会给定 L,R ,然后定义满足
ai∈[L,R] 的点 i 为关键点
你需要回答出满足下列至少一个条件的点 x 的个数:1. x 是关键点2. 在树上删去 x 和所有与其相连的边后,存在两个关键点 a,b ,使得 a 和 b 不连通

数据范围

\[1 \le n \le 10^5 \]

解题思路

神仙 ztb 学长给出了一个神仙的线段树合并做法,我想了想有一共点分树 + lct + 树状数组的方法,又想了一下,点分树完全可以扔了,下面给出一个 \(\Theta(n\log n)\) 的 LCT + 树状数组做法吧

这道题讲的就是求一个包含所有关键点的联通块大小

有两个套路如果知道那么这题将会很好想到

第一个套路:找到 \([L,R]\) 所有点的 \(Lca\),易知 \(Lca\) 肯定在联通块内,且除了 \(Lca\) 子树的节点不会在联通块内,也就是我们直接统计 \([L,R]\)\(Lca\) 的路径之并的大小即可,一开始我想建个点分树,结果发现直接统计 \([L,R]\) 到根的路径再将 \(Lca\) 到根的路径减去即可,求 \(Lca\) 可以用三个 st 表实现

第二个套路:将所有询问离线下来,按 R 排序,我们把所有贡献尽可能的放到靠右的点上,具体来说,用个树状数组维护,加入 R 时,把 R 到根节点的路径长度加到 R 位置上,但这条路径可能会和以前的路径有重合,要把以前的贡献删掉,容易发现用 LCT 可以很好的维护这个问题

所以我们较为简单的实现了这题

#pragma GCC optimize(3, "inline")
#include <queue>
#include <vector>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MP make_pair
#define ll long long
#define fi first
#define se second
using namespace std;

template <typename T>
void read(T &x) {
    x = 0; bool f = 0;
    char c = getchar();
    for (;!isdigit(c);c=getchar()) if (c=='-') f=1;
    for (;isdigit(c);c=getchar()) x=x*10+(c^48);
    if (f) x=-x;
}

template<typename F>
inline void write(F x, char ed = '\n')
{
	static short st[30];short tp=0;
	if(x<0) putchar('-'),x=-x;
	do st[++tp]=x%10,x/=10; while(x);
	while(tp) putchar('0'|st[tp--]);
	putchar(ed);
}

template <typename T>
inline void Mx(T &x, T y) { x < y && (x = y); }

template <typename T>
inline void Mn(T &x, T y) { x > y && (x = y); }

const int N = 200500;

int cnt[N], a[N], c[N];
int lg[N], st[N][18], d[N], n;

inline void Add(int x, int c) {
	for (; x <= n; x += x & -x) d[x] += c;
}

inline int query(int x) {
	int res = 0;
	for (; x; x -= x & -x) res += d[x];
	return res;
}

namespace LCT {
	#define ls son[x][0]
	#define rs son[x][1]
	int son[N][2], f[N], siz[N], col[N];
	bool nroot(int x) {
		return son[f[x]][0] == x || son[f[x]][1] == x;
	}
	void update(int x) {
		siz[x] = siz[ls] + siz[rs] + 1;
	}
	void rotate(int x) {
		int y = f[x], z = f[y];
		int k = son[y][1] == x, w = son[x][!k];
		if (nroot(y)) son[z][son[z][1]==y] = x; f[x] = z;
		f[y] = x, son[x][!k] = y;
		if (w) f[w] = y; son[y][k] = w;
		update(y);
	}
	void spread(int x) {
		if (ls) col[ls] = col[x];
		if (rs) col[rs] = col[x];
	}
	int st[N];
	void splay(int x) {
		int y = x, z = 0; st[++z] = y;
		while (nroot(y)) st[++z] = y = f[y];
		while (z) spread(st[z--]);
		while (nroot(x)) {
			int y = f[x], z = f[y];
			if (nroot(y)) {
				if ((son[z][0] == y) ^ (son[y][0] == x)) rotate(x);
				else rotate(y);
			}
			rotate(x);
		}
		update(x);
	}
	void access(int x, int i) {
		for (int y = 0; x; x = f[y = x]) {
			splay(x); siz[x] -= siz[rs], (rs = y) && (f[y] = x);
			if (col[x]) Add(col[x], -siz[x]);
			Add(i, siz[x]), update(x); col[x] = i;
		}
	}
}

struct St1 {
	int st[N<<1][19], n;
	void init(void) {
		for (int j = 1;j <= 20; j++) 
			for (int i = 1;i + (1 << j) - 1 <= n; i++)
				st[i][j] = min(st[i][j-1], st[i+(1<<(j-1))][j-1]);
	}
	
	inline int query(int l, int r) {
		int t = lg[r - l + 1];
		return min(st[l][t], st[r-(1<<t)+1][t]);
	}

}s1, s2;

struct St2 {
	int st[N][19], n;
	void init(void) {
		for (int j = 1;j <= 20; j++) 
			for (int i = 1;i + (1 << j) - 1 <= n; i++)
				st[i][j] = max(st[i][j-1], st[i+(1<<(j-1))][j-1]);
	}
	
	inline int query(int l, int r) {
		int t = lg[r - l + 1];
		return max(st[l][t], st[r-(1<<t)+1][t]);
	}

}s3;

int dep[N], ne[N<<1], to[N<<1], h[N], tot, cc;
inline void add(int x, int y) {
	ne[++tot] = h[x], to[h[x] = tot] = y;
}

void dfs(int x, int fa) {
	LCT::f[x] = fa, LCT::siz[x] = 1;
	s2.st[++cc][0] = dep[x], s1.st[a[x]][0] = s3.st[a[x]][0] = cc;
	for (int i = h[x]; i; i = ne[i]) {
		int y = to[i]; if (y == fa) continue;
		dep[y] = dep[x] + 1, dfs(y, x);
		s2.st[++cc][0] = dep[x];
	}
}

int Dep(int l, int r) {
	int x = s1.query(l, r), y = s3.query(l, r);
	return s2.query(x, y);
}

vector<pair<int, int> > v[N]; 
int pos[N], ans[N<<2], q;

int main() {
//	freopen ("hs.in","r",stdin);
//	freopen ("hs.out","w",stdout);
	read(n), read(q);
	for (int i = 1;i <= n; i++) read(a[i]), c[i] = a[i];
	sort(c + 1, c + n + 1);
	for (int i = 1;i <= n; i++) {
		a[i] = lower_bound(c + 1, c + n + 1, a[i]) - c;
		cnt[a[i]]++, a[i] += cnt[a[i]] - 1, pos[a[i]] = i;
	}
	for (int i = 1, x, y;i < n; i++) 
		read(x), read(y), add(x, y), add(y, x);
	dfs(1, 0), s2.n = cc, s1.n = s3.n = n;
	for (int i = 2;i <= cc; i++) lg[i] = lg[i>>1] + 1;
	s1.init(), s2.init(), s3.init();
	for (int i = 1, l, r;i <= q; i++) {
		read(l), read(r);
		l = lower_bound(c + 1, c + n + 1, l) - c;
		r = upper_bound(c + 1, c + n + 1, r) - c - 1;
		if (l > r) continue; ans[i] -= Dep(l, r);
		v[r].emplace_back(l, i);
	}
	for (int i = 1;i <= n; i++) {
		LCT::access(pos[i], i);
		for (auto t: v[i]) 
			ans[t.se] += query(i) - query(t.fi - 1);
	}
	for (int i = 1;i <= q; i++) write(ans[i]);
	return 0;
}

posted @ 2020-07-29 21:10  Hs-black  阅读(134)  评论(0编辑  收藏  举报