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;
}