QTREE 系列

  • tps 以下代码均为LCT
    LCT专题???

QTREE 1

树链剖分 or LCT 板子

没啥可说的,这题 LCT 跑的还没树链剖分 + 线段树快

\(code\)

#include<bits/stdc++.h>
using namespace std;
#define rg register
inline int read(){
	rg char ch = getchar();
	rg int x = 0, f = 0;
	while(! isdigit(ch)) f |= (ch == '-'), ch = getchar();
	while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
	return f ? -x : x; 
}
const int N = 2e5 + 5;
int id[N];
int f[N], son[N][2], maxi[N], cnt, v[N], lazy[N], g[N];
inline int new_node(int val){
	v[++cnt] = val;
	maxi[cnt] =val;
	return cnt;
}
#define ls son[p][0]
#define rs son[p][1]
#define fs(p) (son[f[p]][1] == p)
#define nroot(p) (son[f[p]][0] == p || son[f[p]][1] == p)
#define max(a, b) ((a) > (b) ? (a) : (b))
inline void up(int p){
	maxi[p] = max(v[p], max(maxi[ls], maxi[rs]));
}
inline void ros(int p){
	int fa = f[p], ffa = f[fa];
	int c = fs(p);
	if(nroot(fa)) son[ffa][fs(fa)] = p; f[p] = ffa;
	son[fa][c] = son[p][c ^ 1]; f[son[p][c ^ 1]] = fa;
	son[p][c ^ 1] = fa; f[fa] = p;
	up(fa); up(p);
}
int s[N], stop;
inline void addtag(int p){
	swap(ls, rs);
	lazy[p] ^= 1;
}
inline void pushdown(int p){
	if(lazy[p]){
		if(ls) addtag(ls);
		if(rs) addtag(rs);
		lazy[p] = 0;
	}
}
inline void splay(int p){
	int fa;
	s[++stop] = fa = p;
	while(nroot(fa)) s[++stop] = fa = f[fa];
	while(stop) pushdown(s[stop--]);
	while(nroot(p)){
		fa = f[p];
		if(nroot(fa)) ros(fs(fa) ^ fs(p) ? p : fa);
		ros(p);
	}
}
inline void access(int p){
	for(int pl = 0; p; p = f[pl = p])
		splay(p), rs = pl, up(p);
}
inline int findroot(int p){
	access(p);
	splay(p);
	while(ls) pushdown(p), p = ls;
	splay(p);
	return p;
}
inline void makeroot(int p){
	access(p);
	splay(p);
	addtag(p);
}
inline void split(int x, int y){
	makeroot(x);
	access(y);
	splay(y);
}
inline void link(int x, int y){
	makeroot(x);
	if(findroot(y) ^ x) f[x] = y;
}
inline void work(){
	//memset(son, 0, sizeof son); cnt = 0;
	//memset(id, 0, sizeof id);
	int n = read();
	for(int now, x, y, z, i = 1; i < n; ++i){
		x = read(), y = read(), z = read();
		if(!id[x]) id[x] = new_node(0);
		if(!id[y]) id[y] = new_node(0);
		g[i] = now = new_node(z);
		link(id[x], now);
		link(now, id[y]);
	}
	char *s = new char[15];
	scanf("%s", s);
	int x, y, z;
	while(s[0] ^ 'D'){
		if(s[0] == 'Q'){
			x = read(), y = read();
			split(id[x], id[y]);
			printf("%d\n",maxi[id[y]]);
		}else{
			x = read(), z = read();
			makeroot(g[x]);
			v[g[x]] = z;
			up(g[x]);
		}
		scanf("%s", s);
	}
}
		
signed main(){
	work();
	//int T = read();
	//while(T--) work();
	return 0;
}
/*
3
1 2 1
2 3 2
QUERY 1 2
CHANGE 1 3
QUERY 1 2
DONE

1

3
1 2 1
2 3 2
QUERY 1 2
CHANGE 1 3
QUERY 1 2
DONE
*/

QTREE 2

LCT + 平衡树找第 K 大

\(code\)

#include<bits/stdc++.h>
using namespace std;
#define rg register
inline int read(){
	rg char ch = getchar();
	rg int x = 0, f = 0;
	while(! isdigit(ch)) f |= (ch == '-'), ch = getchar();
	while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
	return f ? -x : x; 
}
const int N = 2e5 + 5;
int id[N];
int f[N], son[N][2], sum[N], cnt, v[N], lazy[N], g[N], sz[N], del[N], back[N];
inline int new_node(int val){
	v[++cnt] = val;
	sum[cnt] = val;
	return cnt;
}
#define ls son[p][0]
#define rs son[p][1]
#define fs(p) (son[f[p]][1] == p)
#define nroot(p) (son[f[p]][0] == p || son[f[p]][1] == p)
#define max(a, b) ((a) > (b) ? (a) : (b))
inline void up(int p){
	sum[p] = sum[ls] + sum[rs] + v[p];
	sz[p] = del[p] + sz[ls] + sz[rs];
}
inline void ros(int p){
	int fa = f[p], ffa = f[fa];
	int c = fs(p);
	if(nroot(fa)) son[ffa][fs(fa)] = p; f[p] = ffa;
	son[fa][c] = son[p][c ^ 1]; f[son[p][c ^ 1]] = fa;
	son[p][c ^ 1] = fa; f[fa] = p;
	up(fa); up(p);
}
int s[N], stop;
inline void addtag(int p){
	swap(ls, rs);
	lazy[p] ^= 1;
}
inline void pushdown(int p){
	if(lazy[p]){
		if(ls) addtag(ls);
		if(rs) addtag(rs);
		lazy[p] = 0;
	}
}
inline void splay(int p){
	int fa;
	s[++stop] = fa = p;
	while(nroot(fa)) s[++stop] = fa = f[fa];
	while(stop) pushdown(s[stop--]);
	while(nroot(p)){
		fa = f[p];
		if(nroot(fa)) ros(fs(fa) ^ fs(p) ? p : fa);
		ros(p);
	}
}
inline void access(int p){
	for(int pl = 0; p; p = f[pl = p])
		splay(p), rs = pl, up(p);
}
inline int findroot(int p){
	access(p);
	splay(p);
	while(ls) pushdown(p), p = ls;
	splay(p);
	return p;
}
inline void makeroot(int p){
	access(p);
	splay(p);
	addtag(p);
}
inline void split(int x, int y){
	makeroot(x);
	access(y);
	splay(y);
}
inline void link(int x, int y){
	makeroot(x);
	if(findroot(y) ^ x) f[x] = y;
}
inline int kth(int p, int k){
	while(true){
		if(sz[ls] >= k) p = ls;
		else if(sz[ls] == k - 1 && del[p]) return back[p];
		else k -= sz[ls] + del[p], p = rs;
	}
}
int n;
inline void work(){
	for(int i = 1; i <= cnt; ++i) son[i][0] = son[i][1] = 0; cnt = 0;
	for(int i = 1; i <= n; ++i) id[i] = 0;
	int n = read();
	for(int x, y, z, i = 1; i < n; ++i){
		x = read(), y = read(), z = read();
		if(!id[x]) id[x] = new_node(0), back[cnt] = x, del[cnt] = sz[cnt] = 1;
		if(!id[y]) id[y] = new_node(0), back[cnt] = y, del[cnt] = sz[cnt] = 1;
		g[i] = new_node(z);
		del[cnt] = sz[cnt] = 0;
		link(id[x], g[i]);
		link(g[i], id[y]);
	}
	char *s = new char[15];
	scanf("%s", s);
	int x, y, z;
	while(s[1] ^ 'O'){
		if(s[0] == 'D'){
			x = read(), y = read();
			split(id[x], id[y]);
			printf("%d\n",sum[id[y]]);
		}else{
			x = id[read()], y = id[read()]; z = read();
			split(x, y);
			printf("%d\n", kth(y, z));
		}
		scanf("%s", s);
	}
}
		
signed main(){
	int T = read();
	while(T--) work();
	return 0;
}
/*
*/

QTREE 3

虽然这题可以不翻转, 但我还是写了

记录一个最近点和最远点, 序列翻转的话需要交换这两个值

\(code\)

#include<bits/stdc++.h>
using namespace std;
#define rg register
inline int read(){
	rg char ch = getchar();
	rg int x = 0, f = 0;
	while(! isdigit(ch)) f |= (ch == '-'), ch = getchar();
	while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
	return f ? -x : x; 
}
const int N = 2e5 + 5;
int id[N];
int f[N], son[N][2], find1[N], find2[N], cnt, lazy[N], del[N];
#define ls son[p][0]
#define rs son[p][1]
#define fs(p) (son[f[p]][1] == p)
#define nroot(p) (son[f[p]][0] == p || son[f[p]][1] == p)
#define max(a, b) ((a) > (b) ? (a) : (b))
inline void up(int p){
	if(find1[ls]) find1[p] = find1[ls];
	else if(del[p]) find1[p] = p;
	else find1[p] = find1[rs];
	
	if(find2[rs]) find2[p] = find2[rs];
	else if(del[p]) find2[p] = p;
	else find2[p] = find2[ls];
}
inline void ros(int p){
	int fa = f[p], ffa = f[fa];
	int c = fs(p);
	if(nroot(fa)) son[ffa][fs(fa)] = p; f[p] = ffa;
	son[fa][c] = son[p][c ^ 1]; f[son[p][c ^ 1]] = fa;
	son[p][c ^ 1] = fa; f[fa] = p;
	up(fa); up(p);
}
int s[N], stop;
#define swap(a, b) (a ^= b ^= a ^= b)
inline void addtag(int p){
	swap(ls, rs);
	swap(find1[p], find2[p]);
	lazy[p] ^= 1;
}
inline void pushdown(int p){
	if(lazy[p]){
		if(ls) addtag(ls);
		if(rs) addtag(rs);
		lazy[p] = 0;
	}
}
inline void splay(int p){
	int fa;
	s[++stop] = fa = p;
	while(nroot(fa)) s[++stop] = fa = f[fa];
	while(stop) pushdown(s[stop--]);
	while(nroot(p)){
		fa = f[p];
		if(nroot(fa)) ros(fs(fa) ^ fs(p) ? p : fa);
		ros(p);
	}
	//up(p);
}
inline void access(int p){
	for(int pl = 0; p; p = f[pl = p])
		splay(p), rs = pl, up(p);
}
inline int findroot(int p){
	access(p);
	splay(p);
	while(ls) pushdown(p), p = ls;
	splay(p);
	return p;
}
inline void makeroot(int p){
	access(p);
	splay(p);
	addtag(p);
}
inline void split(int x, int y){
	makeroot(x);
	access(y);
	splay(y);
}
inline void link(int x, int y){
	makeroot(x);
	if(findroot(y) ^ x) f[x] = y;
}
inline void work(){
	int n = read(), m = read();
	for(int x, y, i = 1; i < n; ++i){
		x = read(), y = read();
		link(x, y);
	}
	int s;
	int x;
	while(m--){
		s = read();
		if(s){
			x = read();
			split(1, x);
			printf("%d\n", find1[x] ? find1[x] : -1);
		}else{
			x = read();
			access(x);
			splay(x);
			del[x] ^= 1;
			up(x);
		}
	}
			
}
		
signed main(){
	work();
	return 0;
}

QTREE 4

动态点分治 + 堆

建出点分树后每个点放个堆

删除的话就开两个堆, 懒惰删除

QTREE 5

这题需要维护子树最小值, 加上翻转后维护过于麻烦, 考虑不翻转

维护两个值

1 -> \(lmn\) : 在当前链上最高的点到一个白点的最近距离

2 -> \(rmn\) : 在当前链上最低的点到一个白点的最近距离

这样当一个点( \(x\) )和根连接成链( \(access\) )的时候,\(x\) 就是最低点, \(rmn\) 就是答案

为了求出这个, 我们还要维护子树最小值, 就个对每个点开个 \(multiset\)

\(code\)

#include<iostream>
#include<cctype>
#include<cstdio>
#include<cstring>
#include<set>
using namespace std;
#define rg register
inline int read(){
	rg char ch = getchar();
	rg int x = 0, f = 0;
	while(! isdigit(ch)) f |= (ch == '-'), ch = getchar();
	while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
	return f ? -x : x;
}
const int N = 1e5 + 5;
int son[N][2], f[N], lmn[N], rmn[N], sz[N], col[N];
multiset<int> s[N];
int head[N], nxt[N << 1], ver[N << 1], tot;
inline void add(int x, int y){
	ver[++tot] = y;
	nxt[tot] = head[x];
	head[x] = tot;
}
#define ls son[p][0]
#define rs son[p][1]
#define fs(p) (son[f[p]][1] == p)
#define nroot(p) (son[f[p]][1] == p || son[f[p]][0] == p)
#define inf 0x3f3f3f3f
inline int get(int p){
	return s[p].empty() ? inf : *s[p].begin();
}
inline int max(int a, int b){ return a > b ? a : b; }
inline int min(int a, int b){ return a < b ? a : b; }
inline void up(int p){
	sz[p] = sz[ls] + sz[rs] + 1;
	lmn[p] = min(lmn[ls], sz[ls] + min(get(p), min(col[p] ? 0 : inf, lmn[rs] + 1)));
	rmn[p] = min(rmn[rs], sz[rs] + min(get(p), min(col[p] ? 0 : inf, rmn[ls] + 1)));
}
inline void ros(int p){
	int fa = f[p], ffa = f[fa];
	int c = fs(p);
	if(nroot(fa)) son[ffa][fs(fa)] = p; f[p] = ffa;
	son[fa][c] = son[p][c ^ 1]; f[son[p][c ^ 1]] = fa;
	son[p][c ^ 1] = fa; f[fa] = p;
	up(fa); // up(p);
}
inline void splay(int p){
	int fa;
	while(nroot(p)){
		fa = f[p];
		if(nroot(fa)) ros(fs(fa) ^ fs(p) ? p : fa);
		ros(p);
	}
	up(p);
}
inline void access(int p){
	for(int y = 0; p; p = f[y = p]){
		splay(p);
		if(rs) s[p].insert(lmn[rs] + 1);
		if(y) s[p].erase(s[p].find(lmn[y] + 1));
		rs = y;
		up(p);
	}
}
void dfs(int x){
	sz[x] = 1;
	for(int y , i = head[x]; i; i = nxt[i]){
		if((y = ver[i]) == f[x]) continue;
		s[x].insert(inf + 1);
		f[y] = x;
		dfs(y);
	}
}
int n, m;
int main(){
	n = read();
	for(int x, y, i = 1; i < n; ++i){
		x = read(), y = read();
		add(x, y); add(y, x);
	}
	memset(lmn, 0x3f, sizeof lmn);
	memset(rmn, 0x3f, sizeof rmn);	
	dfs(1);
	m = read();
	int op, x;
	while(m--){
		op = read(), x = read();
		switch(op){
			case 0 :
				access(x);
				splay(x);
				col[x] ^= 1;
				up(x);
				break;
			case 1 :
				access(x);
				splay(x);
				printf("%d\n", rmn[x] >= inf ? -1 : rmn[x]);
		}
	}
			
	return 0;
}

QTREE 6

LCT 维护染色联通块, 连通块大小就是答案

先考虑暴力

开两颗 LCT, 维护子树大小

当一个点改变颜色时, 就把它和与它相连的同色点断开, 异色点连接, 改变颜色

如果图是菊花图, 这种做法就去世了

正解

通过暴力, 我们有了初步想法, 要通过维护子树大小维护连通块大小

既然点不行, 就考虑边

让一个点父亲边成为它的颜色, 把同一种颜色的边放到同一棵 LCT 上, 这样对于其中一棵 LCT 上的一颗树, 除了根节点以外其它都是同一种颜色

我们查询只需要把 \(x\) \(access\) , 找到 \(x\) 的根, 这时根的右儿子的大小就是答案

因为 1 号节点没父亲边, 我们为了方便维护就给他加个假父亲

\(code\)

#include<iostream>
#include<cstdio>
#include<cctype>
#include<cstdlib>
using namespace std;
#define rg register
inline int read(){
	rg char ch = getchar();
	rg int x = 0, f = 0;
	while(! isdigit(ch)) f |= (ch == '-'), ch = getchar();
	while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
	return f ? -x : x;
}
const int N = 1e5 + 5, M = N << 1;
int head[N], nxt[M], ver[M], tot, col[N], fa[N];
inline void add(int x, int y){
	ver[++tot] = y;
	nxt[tot] = head[x];
	head[x] = tot;
}
struct LCT{
	int son[N][2], f[N], sz[N], szl[N];
	#define ls son[p][0]
	#define rs son[p][1]
	#define fs(p) (son[f[p]][1] == p)
	#define nroot(p) (son[f[p]][0] == p || son[f[p]][1] == p)
	inline void up(int p){
		sz[p] = sz[ls] + sz[rs] + szl[p] + 1;
	}
	inline void prepro(int n){
		for(int i = 1; i <= n; ++i) sz[i] = 1;
	}
	inline void ros(int p){
		int fa = f[p], ffa = f[fa];
		int c = fs(p);
		if(nroot(fa)) son[ffa][fs(fa)] = p; f[p] = ffa;
		son[fa][c] = son[p][c ^ 1]; f[son[p][c ^ 1]] = fa;
		son[p][c ^ 1] = fa; f[fa] = p;
		up(fa);
	}
	inline void splay(int p){
		static int fa;
		while(nroot(p)){
			fa = f[p];
			if(nroot(fa)) ros(fs(fa) ^ fs(p) ? p : fa);
			ros(p);
		}
		up(p);
	}
	inline void access(int p){
		for(int y = 0; p; p = f[y = p]){
			splay(p);
			szl[p] += sz[rs];
			szl[p] -= sz[y];
			rs = y;
			up(p);
		}
	}
	inline int findroot(int p){
		access(p);
		splay(p);
		while(ls) p = ls;
		splay(p);
		return p;
	}
	inline void link(int x){
		splay(x);
		int y = f[x] = fa[x];
		access(y);
		splay(y);
		szl[y] += sz[x]; sz[y] += sz[x];
	}
	inline void cut(int p){
		access(p);
		splay(p);
		ls = f[ls] = 0;
		up(p);
	}
	inline int query(int x){
		x = findroot(x);
		return sz[son[x][1]];
	}
}lct[2];
int n, m;
void dfs(int x){
	for(int y, i = head[x]; i; i = nxt[i]){
		y = ver[i];
		if(y == fa[x]) continue;
		fa[y] = x;
		dfs(y);
		lct[0].link(y);
	}
}
signed main(){
	n = read();
	for(int x, y, i = 1; i < n; ++i){
		x = read(), y = read();
		add(x, y);
		add(y, x);
	}
	dfs(1);
	lct[0].prepro(n + 1);
	lct[1].prepro(n + 1);
	fa[1] = n + 1; lct[0].link(1);
	m = read();
	int op, x;
	while(m--){
		op = read(), x = read();
		switch(op){
			case 0:
				printf("%d\n", lct[col[x]].query(x));
				break;
			case 1 :
				lct[col[x]].cut(x);
				col[x] ^= 1;
				lct[col[x]].link(x);
		}
	}
				
	return 0;
}

QTREE 7

和 QTREE 6 一样, 还是开两颗 LCT

只不过变成维护子树最值了, 用维护子树最值的方法就行了

注意 \(val \in [-1e9, 1e9]\) 所以 0 号点的 \(maxi\) 要取 \(-inf\)

更改 \(val\) 时记着改两个树里的

\(code\)

#include<iostream>
#include<cstdio>
#include<cctype>
#include<set>
#include<cstring>
using namespace std;
#define rg register
inline int read(){
	rg char ch = getchar();
	rg int x = 0, f = 0;
	while(! isdigit(ch)) f |= (ch == '-'), ch = getchar();
	while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
	return f ? -x : x;
}	
const int N = 1e5 + 5;
int head[N], nxt[N << 1], ver[N << 1], tot;
int fa[N], col[N];
inline void add(int x, int y){
	ver[++tot] = y;
	nxt[tot] = head[x];
	head[x] = tot;
}
#define inf 0x3f3f3f3f
struct LCT{
	int son[N][2], f[N], v[N], maxi[N];
	multiset<int> s[N];
	LCT(){ maxi[0] = -inf; }
	inline int find(int p){
		return s[p].empty() ? -inf : *s[p].rbegin();
	}
	#define ls son[p][0]
	#define rs son[p][1]
	#define fs(p) (son[f[p]][1] == p)
	#define nroot(p) (son[f[p]][0] == p || son[f[p]][1] == p)
	inline void up(int p){
		maxi[p] = max(max(v[p], max(maxi[ls], maxi[rs])), find(p));
	}
	inline void ros(int p){
		int fa = f[p], ffa = f[fa];
		int c = fs(p);
		if(nroot(fa)) son[ffa][fs(fa)] = p; f[p] = ffa;
		son[fa][c] = son[p][c ^ 1]; f[son[p][c ^ 1]] = fa;
		son[p][c ^ 1] = fa; f[fa] = p;
		up(fa);
	}
	inline void splay(int p){
		static int fa;
		while(nroot(p)){
			fa = f[p];
			if(nroot(fa)) ros(fs(fa) ^ fs(p) ? p : fa);
			ros(p);
		}
		up(p);
	}
	inline void access(int p){
		for(int y = 0; p; p = f[y = p]){
			splay(p);
			if(rs) s[p].insert(maxi[rs]);
			if(y) s[p].erase(s[p].find(maxi[y]));
			rs = y;
			up(p);
		}
	}
	inline void link(int p){
		access(p);
		splay(p);
		int pl = f[p] = fa[p];
		access(pl);
		splay(pl);
		son[pl][1] = p;
		up(pl);
	}
	inline void cut(int p){
		access(p);
		splay(p);
		ls = f[ls] = 0;
		up(p);
	}
	inline int findroot(int p){
		access(p);
		splay(p);
		while(ls) p = ls;
		splay(p);
		return p;
	}
	inline int query(int p){
		p = findroot(p);
		return maxi[rs];
	}
	inline void change(int p, int val){
		access(p);
		splay(p);
		v[p] = val;
		up(p);
	}
}lct[2];
void dfs(int x){
	for(int y, i = head[x]; i; i = nxt[i]){
		y = ver[i];
		if(y == fa[x]) continue;
		fa[y] = x;
		lct[col[y]].link(y);
		dfs(y);
	}
}
int n, m;
signed main(){
	n = read();
	for(int x, y, i = 1; i < n; ++i){
		x = read(), y = read();
		add(x, y);
		add(y, x);
	}
	for(int i = 1; i <= n; ++i) col[i] = read();
	for(int i = 1; i <= n; ++i) lct[0].maxi[i] = lct[0].v[i] = lct[1].maxi[i] = lct[1].v[i] = read();
	dfs(1);
	fa[1] = n + 1; lct[col[1]].link(1);
	m = read();
	int op, x, d;
	while(m--){
		op = read(), x = read();
		switch(op){
			case 0 : printf("%d\n", lct[col[x]].query(x)); break;
			case 1 : lct[col[x]].cut(x); lct[col[x] ^= 1].link(x); break;
			case 2 : d = read(); lct[0].change(x, d), lct[1].change(x, d);
		}
	}
	return 0;
}

完结撒花

posted @ 2020-06-06 12:31  __int256  阅读(191)  评论(0编辑  收藏  举报