Qtree1

将边权变为这条边连接的两个点中深度更深的点的点权,这样就可以变为带修改链上最大点权。直接树链剖分即可。

下面是一份C语言代码

#include<stdio.h>
#include<string.h>
#define MAXN 10001
inline int read(){
int a = 0;
int f = 0;
char c = getchar();
while(!isdigit(c)){
if(c == '-')
f = 1;
c = getchar();
}
while(isdigit(c)){
a = (a << 3) + (a << 1) + (c ^ '0');
c = getchar();
}
return f ? -a : a;
} char output[20];
inline void print(int x){
int dirN = 18;
if(x == 0)
fwrite("0" , sizeof(char) , 1 , stdout);
else{
if(x < 0){
x = -x;
fwrite("-" , sizeof(char) , 1 , stdout);
}
while(x){
output[--dirN] = x % 10 + 48;
x /= 10;
}
fwrite(output + dirN , 1 , strlen(output + dirN) , stdout);
}
fwrite("\n" , 1 , 1 , stdout);
} inline int max(int a , int b){
return a > b ? a : b;
} struct node{
int l , r , maxN;
}Tree[MAXN << 2];
struct Edge{
int end , upEd , w;
}Ed[MAXN << 1];
int head[MAXN] , start[MAXN << 1] , size[MAXN] , son[MAXN] , fa[MAXN] , dep[MAXN];
int top[MAXN] , ind[MAXN] , rk[MAXN] , val[MAXN] , N , cntEd , ts; inline void addEd(int a , int b , int c){
Ed[++cntEd].end = b;
Ed[cntEd].upEd = head[a];
Ed[cntEd].w = c;
head[a] = cntEd;
} void dfs1(int dir , int father , int w){
val[dir] = w;
dep[dir] = dep[fa[dir] = father] + 1;
size[dir] = 1;
int i;
for(i = head[dir] ; i ; i = Ed[i].upEd)
if(!dep[Ed[i].end]){
dfs1(Ed[i].end , dir , Ed[i].w);
size[dir] += size[Ed[i].end];
if(size[son[dir]] < size[Ed[i].end])
son[dir] = Ed[i].end;
}
} void dfs2(int dir , int t){
top[dir] = t;
rk[ind[dir] = ++ts] = dir;
if(!son[dir])
return;
dfs2(son[dir] , t);
int i;
for(i = head[dir] ; i ; i = Ed[i].upEd)
if(Ed[i].end != fa[dir] && Ed[i].end != son[dir])
dfs2(Ed[i].end , Ed[i].end);
} inline void pushup(int dir){
Tree[dir].maxN = max(Tree[dir << 1].maxN , Tree[dir << 1 | 1].maxN);
} void init(int dir , int l , int r){
Tree[dir].l = l;
Tree[dir].r = r;
if(l == r)
Tree[dir].maxN = val[rk[l]];
else{
init(dir << 1 , l , l + r >> 1);
init(dir << 1 | 1 , (l + r >> 1) + 1 , r);
pushup(dir);
}
} void change(int dir , int tar , int val){
if(Tree[dir].l == Tree[dir].r){
Tree[dir].maxN = val;
return;
}
if(Tree[dir].l + Tree[dir].r >> 1 >= tar)
change(dir << 1 , tar , val);
else
change(dir << 1 | 1 , tar , val);
pushup(dir);
} int findMax(int dir , int l , int r){
if(Tree[dir].l >= l && Tree[dir].r <= r)
return Tree[dir].maxN;
int maxN = 0;
if(l <= Tree[dir].l + Tree[dir].r >> 1)
maxN = max(maxN , findMax(dir << 1 , l , r));
if(r > Tree[dir].l + Tree[dir].r >> 1)
maxN = max(maxN , findMax(dir << 1 | 1 , l , r));
return maxN;
} inline void work(int x , int y){
if(x == y){
print(0);
return;
}
int tx = top[x] , ty = top[y] , maxN = -0x7fffffff;
while(tx != ty)
if(dep[tx] >= dep[ty]){
maxN = max(maxN , findMax(1 , ind[tx] , ind[x]));
x = fa[tx];
tx = top[x];
}
else{
maxN = max(maxN , findMax(1 , ind[ty] , ind[y]));
y = fa[ty];
ty = top[y];
}
if(ind[x] < ind[y])
maxN = max(maxN , findMax(1 , ind[x] + 1 , ind[y]));
if(ind[y] < ind[x])
maxN = max(maxN , findMax(1 , ind[y] + 1 , ind[x]));
print(maxN);
} int cmp(int a , int b){
return dep[a] < dep[b] ? ind[b] : ind[a];
} char s[10];
int main(){
int T;
for(T = read() ; T ; T--){
N = read();
memset(head , 0 , sizeof(head));
memset(dep , 0 , sizeof(dep));
memset(start , 0 , sizeof(start));
memset(size , 0 , sizeof(size));
memset(son , 0 , sizeof(son));
memset(fa , 0 , sizeof(fa));
memset(top , 0 , sizeof(top));
memset(ind , 0 , sizeof(ind));
memset(rk , 0 , sizeof(rk));
memset(val , 0 , sizeof(val));
cntEd = ts = 0;
int i;
for(i = 1 ; i < N ; i++){
start[(i << 1) - 1] = read();
start[i << 1] = read();
int c = read();
addEd(start[(i << 1) - 1] , start[i << 1] , c);
addEd(start[i << 1] , start[(i << 1) - 1] , c);
}
dfs1(1 , 0 , 0);
dfs2(1 , 1);
init(1 , 1 , N);
while(scanf("%s" , s) && s[0] != 'D'){
int a = read() , b = read();
if(s[0] == 'Q')
work(a , b);
else
change(1 , cmp(start[a << 1] , start[(a << 1) - 1]) , b);
}
}
return 0;
}

Qtree2

同样先把边权变为较深的点的点权,那么这两个操作都可以通过倍增+维护倍增经过的所有点的点权和完成。

    #include<bits/stdc++.h>
//This code is written by Itst
using namespace std; inline int read(){
int a = 0;
bool f = 0;
char c = getchar();
while(c != EOF && !isdigit(c)){
if(c == '-')
f = 1;
c = getchar();
}
while(c != EOF && isdigit(c)){
a = (a << 3) + (a << 1) + (c ^ '0');
c = getchar();
}
return f ? -a : a;
} const int MAXN = 10010;
struct Edge{
int end , upEd , w;
}Ed[MAXN << 1];
int jump[MAXN][21][2] , head[MAXN] , dep[MAXN];
int N , cntEd; inline void addEd(int a , int b , int c){
Ed[++cntEd].end = b;
Ed[cntEd].upEd = head[a];
Ed[cntEd].w = c;
head[a] = cntEd;
} void dfs(int x , int f){
jump[x][0][0] = f;
dep[x] = dep[f] + 1;
for(int i = 1 ; jump[x][i - 1][0] ; ++i){
jump[x][i][0] = jump[jump[x][i - 1][0]][i - 1][0];
jump[x][i][1] = jump[x][i - 1][1] + jump[jump[x][i - 1][0]][i - 1][1];
}
for(int i = head[x] ; i ; i = Ed[i].upEd)
if(Ed[i].end != f){
jump[Ed[i].end][0][1] = Ed[i].w;
dfs(Ed[i].end , x);
}
} inline pair < int , int > LCA(int x , int y){
int sum = 0;
if(dep[x] < dep[y])
swap(x , y);
for(int i = 20 ; i >= 0 ; --i)
if(dep[x] - (1 << i) >= dep[y]){
sum += jump[x][i][1];
x = jump[x][i][0];
}
if(x == y)
return make_pair(x , sum);
for(int i = 20 ; i >= 0 ; --i)
if(jump[x][i][0] != jump[y][i][0]){
sum += jump[x][i][1] + jump[y][i][1];
x = jump[x][i][0];
y = jump[y][i][0];
}
return make_pair(jump[x][0][0] , sum + jump[x][0][1] + jump[y][0][1]);
} inline int Kth(int x , int y , int k){
int t = LCA(x , y).first;
if(dep[x] - dep[t] + 1 >= k){
--k;
for(int i = 16 ; i >= 0 ; --i)
if(k & (1 << i))
x = jump[x][i][0];
return x;
}
else{
k = dep[x] + dep[y] - (dep[t] << 1) + 1 - k;
for(int i = 16 ; i >= 0 ; --i)
if(k & (1 << i))
y = jump[y][i][0];
return y;
}
} inline char getc(){
char c = getchar();
while(!isupper(c))
c = getchar();
return c = getchar();
} int main(){
for(int T = read() ; T ; --T){
memset(head , 0 , sizeof(head));
memset(jump , 0 , sizeof(jump));
cntEd = 0;
N = read();
for(int i = 1 ; i < N ; ++i){
int a = read() , b = read() , c = read();
addEd(a , b , c);
addEd(b , a , c);
}
dfs(1 , 0);
int a , b , c;
bool f = 1;
while(f)
switch(getc()){
case 'I':
printf("%d\n" , LCA(read() , read()).second);
break;
case 'T':
a = read();
b = read();
c = read();
printf("%d\n" , Kth(a , b , c));
break;
default:
f = 0;
}
cout << endl;
}
return 0;
}

Qtree3

一样是树链剖分,线段树上每一个点维护这一段区间中dfn序最大的黑点的dfn序。

#include<bits/stdc++.h>
#define MAXN 100001
using namespace std; namespace IO{
const int maxn((1 << 21) + 1);
char ibuf[maxn], *iS, *iT, obuf[maxn], *oS = obuf, *oT = obuf + maxn - 1, c, st[55];
int f, tp;
char Getc() {
return (iS == iT ? (iT = (iS = ibuf) + fread(ibuf, 1, maxn, stdin), (iS == iT ? EOF : *iS++)) : *iS++);
}
void Flush() {
fwrite(obuf, 1, oS - obuf, stdout);
oS = obuf;
}
void Putc(char x) {
*oS++ = x;
if (oS == oT) Flush();
}
template <class Int> void Input(Int &x) {
for (f = 1, c = Getc(); c < '0' || c > '9'; c = Getc()) f = c == '-' ? -1 : 1;
for (x = 0; c <= '9' && c >= '0'; c = Getc()) x = (x << 3) + (x << 1) + (c ^ 48);
x *= f;
}
template <class Int> void Print(Int x) {
if (!x) Putc('0');
if (x < 0) Putc('-'), x = -x;
while (x) st[++tp] = x % 10 + '0', x /= 10;
while (tp) Putc(st[tp--]);
}
void Getstr(char *s, int &l) {
for (c = Getc(); c < 'a' || c > 'z'; c = Getc());
for (l = 0; c <= 'z' && c >= 'a'; c = Getc()) s[l++] = c;
s[l] = 0;
}
void Putstr(const char *s) {
for (int i = 0, n = strlen(s); i < n; ++i) Putc(s[i]);
}
}
using namespace IO; struct node{
int l , r , minN;
}Tree[MAXN << 2];
struct Edge{
int end , upEd;
}Ed[MAXN << 1];
int son[MAXN] , size[MAXN] , fa[MAXN] , dep[MAXN] , head[MAXN];
int top[MAXN] , ind[MAXN] , rk[MAXN] , N , cntEd , ts; inline void addEd(int a , int b){
Ed[++cntEd].end = b;
Ed[cntEd].upEd = head[a];
head[a] = cntEd;
} void dfs1(int dir , int father){
size[dir] = 1;
dep[dir] = dep[fa[dir] = father] + 1;
for(int i = head[dir] ; i ; i = Ed[i].upEd)
if(!dep[Ed[i].end]){
dfs1(Ed[i].end , dir);
size[dir] += size[Ed[i].end];
if(size[son[dir]] < size[Ed[i].end])
son[dir] = Ed[i].end;
}
} void dfs2(int dir , int t){
top[dir] = t;
rk[ind[dir] = ++ts] = dir;
if(!son[dir])
return;
dfs2(son[dir] , t);
for(int i = head[dir] ; i ; i = Ed[i].upEd)
if(Ed[i].end != son[dir] && Ed[i].end != fa[dir])
dfs2(Ed[i].end , Ed[i].end);
} inline int min(int a , int b){
return a < b ? a : b;
} void init(int dir , int l , int r){
Tree[dir].l = l;
Tree[dir].r = r;
if(l == r)
Tree[dir].minN = 999999;
else{
init(dir << 1 , l , (l + r) >> 1);
init(dir << 1 | 1 , ((l + r) >> 1) + 1 , r);
Tree[dir].minN = min(Tree[dir << 1].minN , Tree[dir << 1 | 1].minN);
}
} void change(int dir , int tar){
if(Tree[dir].l == Tree[dir].r){
Tree[dir].minN = Tree[dir].minN == 999999 ? Tree[dir].l : 999999;
return;
}
if(tar <= (Tree[dir].l + Tree[dir].r) >> 1)
change(dir << 1 , tar);
else
change(dir << 1 | 1 , tar);
Tree[dir].minN = min(Tree[dir << 1].minN , Tree[dir << 1 | 1].minN);
} int findMin(int dir , int l , int r){
if(Tree[dir].l >= l && Tree[dir].r <= r)
return Tree[dir].minN;
int minN;
if(l <= (Tree[dir].l + Tree[dir].r) >> 1){
minN = findMin(dir << 1 , l , r);
if(minN != 999999)
return minN;
}
if(r > (Tree[dir].l + Tree[dir].r) >> 1)
return findMin(dir << 1 | 1 , l , r);
return 999999;
} inline int work(int tar){
int minN = 999999;
while(top[tar] != 1){
minN = min(minN , findMin(1 , ind[top[tar]] , ind[tar]));
tar = fa[top[tar]];
}
minN = min(minN , findMin(1 , 1 , ind[tar]));
return minN == 999999 ? -1 : rk[minN];
} int main(){
int N , M;
Input(N);
Input(M);
for(int i = 1 ; i < N ; i++){
int a , b;
Input(a);
Input(b);
addEd(a , b);
addEd(b , a);
}
dfs1(1 , 0);
dfs2(1 , 1);
init(1 , 1 , N);
while(M--){
int a;
Input(a);
if(a == 0){
Input(a);
change(1 , ind[a]);
}
else{
Input(a);
Print(work(a));
Putc('\n');
}
}
Flush();
return 0;
}

Qtree4

动态点分的学习笔记里面

Qtree5

仍然是动态点分,对于每一个点维护其所有点分树子树中的白点到当前点的最短距离。修改和查询都暴力跳点分树。

注意到从一个子树中走到分治中心再走回去一定不优,所以并不需要维护到父亲的堆。

#include<bits/stdc++.h>
#define INF (int)1e9
//This code is written by Itst
using namespace std; inline int read(){
int a = 0;
bool f = 0;
char c = getchar();
while(c != EOF && !isdigit(c)){
if(c == '-')
f = 1;
c = getchar();
}
while(c != EOF && isdigit(c)){
a = (a << 3) + (a << 1) + (c ^ '0');
c = getchar();
}
return f ? -a : a;
} const int MAXN = 100010;
struct Edge{
int end , upEd;
}Ed[MAXN << 1];
int head[MAXN] , dep[MAXN] , fir[MAXN] , ST[21][MAXN << 1] , logg2[MAXN << 1] , fa[MAXN][20] , size[MAXN] , dis[MAXN][20];
int N , nowSize , minSize , minInd , cntST , cntEd;
bool vis[MAXN] , col[MAXN];
struct pq{
priority_queue < int , vector < int > , greater < int > > q1 , q2; inline void maintain(){
while(!q1.empty() && !q2.empty() && q1.top() == q2.top()){
q1.pop();
q2.pop();
}
} inline void push(int x){
q1.push(x);
} inline void pop(int x){
q2.push(x);
} inline int top(){
maintain();
return q1.empty() ? INF : q1.top();
} }cur[MAXN]; inline void addEd(int a , int b){
Ed[++cntEd].end = b;
Ed[cntEd].upEd = head[a];
head[a] = cntEd;
} void init_dfs(int x , int pre){
dep[x] = dep[pre] + 1;
fir[x] = ++cntST;
ST[0][cntST] = x;
for(int i = head[x] ; i ; i = Ed[i].upEd)
if(Ed[i].end != pre){
init_dfs(Ed[i].end , x);
ST[0][++cntST] = x;
}
} inline int cmp(int a , int b){
return dep[a] < dep[b] ? a : b;
} void init_st(){
for(int i = 2 ; i <= cntST ; ++i)
logg2[i] = logg2[i >> 1] + 1;
for(int i = 1 ; 1 << i <= cntST ; ++i)
for(int j = 1 ; j + (1 << i) - 1 <= cntST ; ++j)
ST[i][j] = cmp(ST[i - 1][j] , ST[i - 1][j + (1 << (i - 1))]);
} inline int LCA(int x , int y){
x = fir[x];
y = fir[y];
if(x > y)
swap(x , y);
int t = logg2[y - x + 1];
return cmp(ST[t][x] , ST[t][y - (1 << t) + 1]);
} inline int calcLen(int x , int y){
return dep[x] + dep[y] - (dep[LCA(x , y)] << 1);
} void getSize(int x){
vis[x] = 1;
++nowSize;
for(int i = head[x] ; i ; i = Ed[i].upEd)
if(!vis[Ed[i].end])
getSize(Ed[i].end);
vis[x] = 0;
} void getRoot(int x){
int maxN = 0;
vis[x] = size[x] = 1;
for(int i = head[x] ; i ; i = Ed[i].upEd)
if(!vis[Ed[i].end]){
getRoot(Ed[i].end);
size[x] += size[Ed[i].end];
maxN = max(maxN , size[Ed[i].end]);
}
maxN = max(maxN , nowSize - size[x]);
if(maxN < minSize){
minSize = maxN;
minInd = x;
}
vis[x] = 0;
} void init_dfz(int x , int p){
nowSize = 0;
minSize = 0x7fffffff;
getSize(x);
getRoot(x);
x = minInd;
vis[x] = 1;
fa[x][0] = p;
for(int i = 0 ; fa[x][i] ; ++i){
fa[x][i + 1] = fa[fa[x][i]][0];
dis[x][i] = calcLen(x , fa[x][i]);
}
for(int i = head[x] ; i ; i = Ed[i].upEd)
if(!vis[Ed[i].end])
init_dfz(Ed[i].end , x);
} void init(){
init_dfs(1 , 0);
init_st();
init_dfz(1 , 0);
} inline int query(int x){
int minN = cur[x].top();
for(int i = 0 ; fa[x][i] ; ++i)
minN = min(minN , cur[fa[x][i]].top() + dis[x][i]);
return minN == INF ? -1 : minN;
} inline void modify(int x){
vis[x] ^= 1;
vis[x] ? cur[x].pop(0) : cur[x].push(0);
for(int i = 0 ; fa[x][i] ; ++i)
vis[x] ? cur[fa[x][i]].pop(dis[x][i]) : cur[fa[x][i]].push(dis[x][i]);
} int main(){
N = read();
for(int i = 1 ; i < N ; ++i){
int a = read() , b = read();
addEd(a , b);
addEd(b , a);
}
init();
for(int M = read() ; M ; --M)
if(read())
printf("%d\n" , query(read()));
else
modify(read());
return 0;
}

Qtree6

考虑LCT。维护点的颜色连通块似乎不好做,因为是否连通实际上是和边相关的一个东西。我们考虑每一个点,当这个点是黑色的时候在黑色的LCT上将它和它的父亲连边,否则在白色的LCT上连边。这样对于黑白两色的LCT,去掉每一个连通块的root后,剩余的每一个连通块就是当前颜色的一个颜色连通块。

那么我们按照这个进行Link、Cut,并在每一次询问的时候先access保证当前点在整棵树的实链上,然后findroot并将root splay至根,最后输出根的右儿子的size即可。

值得注意的是在Link和Cut中不能够进行平常需要在其中进行的makeroot操作,因为这样会改变每一个连通块的根。我们可以这样修改:

Link:每一次进行Link操作的点都一定是当前连通块的根,所以直接splay至LCT的根即可;

Cut:因为每一次要cut的边连接的两个点的父子关系确定,所以可以不进行makeroot,直接对子节点access、splay并去掉对应的边。

至于如何维护子树size,可以维护一个变量表示当前点的虚子树size和,不难发现只有在link和access的时候会改变这个量,这样就可以进行整个子树的维护。值得注意的是维护虚子树信息在link的时候要将父节点先access、splay到LCT的根,这样可以避免更新子树大小之后的一系列pushup。

#include<bits/stdc++.h>
using namespace std; int read(){
int a = 0; char c = getchar(); bool f = 0;
while(!isdigit(c)){f = c == '-'; c = getchar();}
while(isdigit(c)){
a = a * 10 + c - 48; c = getchar();
}
return f ? -a : a;
} const int _ = 2e5 + 3;
int fa[_] , ch[_][2] , sz[_] , vir[_]; bool rmrk[_]; struct Edge{int end , upEd;}Ed[_ << 1];
int pre[_] , head[_] , col[_] , cntEd , N , M;
void addEd(int a , int b){Ed[++cntEd] = (Edge){b , head[a]}; head[a] = cntEd;}
void dfs(int x , int p){pre[x] = p; for(int i = head[x] ; i ; i = Ed[i].upEd) if(Ed[i].end != p) dfs(Ed[i].end , x);} bool nroot(int x){return ch[fa[x]][0] == x || ch[fa[x]][1] == x;}
bool son(int x){return ch[fa[x]][1] == x;}
void up(int x){sz[x] = sz[ch[x][0]] + sz[ch[x][1]] + 1 + vir[x];} void rot(int x){
bool f = son(x); int y = fa[x] , z = fa[y] , w = ch[x][f ^ 1];
fa[x] = z; if(nroot(y)) ch[z][son(y)] = x;
fa[y] = x; ch[x][f ^ 1] = y;
ch[y][f] = w; if(w) fa[w] = y;
up(y);
} void splay(int x){while(nroot(x)){if(nroot(fa[x])) rot(son(x) == son(fa[x]) ? fa[x] : x); rot(x);} up(x);}
void access(int x){
for(int y = 0 ; x ; y = x , x = fa[x]){
splay(x); vir[x] = vir[x] - sz[y] + sz[ch[x][1]];
ch[x][1] = y; up(x);
}
}
int fdrt(int x){access(x); splay(x); while(ch[x][0]) x = ch[x][0]; splay(x); return x;}
void link(int x){splay(x); int y = fa[x] = pre[x]; access(y); splay(y); vir[y] += sz[x]; up(y);}
void cut(int x){access(x); splay(x); ch[x][0] = fa[ch[x][0]] = 0; up(x);}
int qry(int x){x += col[x] * N; access(x); splay(x); splay(x = fdrt(x)); return sz[ch[x][1]];} int main(){
N = read();
for(int i = 1 ; i < N ; ++i){int a = read() , b = read(); addEd(a , b); addEd(b , a);}
dfs(1 , 0); pre[1] = ++N;
for(int i = 1 ; i <= 2 * N ; ++i) sz[i] = 1;
for(int i = 1 ; i < N ; ++i){link(i); pre[i + N] = pre[i] + N;}
for(M = read() ; M ; --M)
if(read()){int id = read(); cut(id + N * col[id]); link(id + N * (col[id] ^= 1));}
else printf("%d\n" , qry(read()));
return 0;
}

Qtree7

考虑在Qtree6的基础上支持子树最值。

考虑维护所有虚子树的最大值,但是注意到我们还可能进行虚实转换使得虚子树变成实子树、不对当前点的虚子树max产生贡献,那么就不能够只使用一个标记维护max。

考虑使用multiset维护一个点的虚子树的max构成的集合,那么在虚实更换的时候对这个multiset进行插入、删除即可进行更新。

复杂度\(O(qlog^2n)\)

#include<bits/stdc++.h>
using namespace std; int read(){
int a = 0; char c = getchar(); bool f = 0;
while(!isdigit(c)){f = c == '-'; c = getchar();}
while(isdigit(c)){
a = a * 10 + c - 48; c = getchar();
}
return f ? -a : a;
} const int _ = 2e5 + 7;
struct Edge{int end , upEd;}Ed[_ << 1];
int head[_] , N , cntEd , pre[_] , col[_]; void addEd(int a , int b){Ed[++cntEd] = (Edge){b , head[a]}; head[a] = cntEd;}
void dfs(int x , int p){pre[x] = p; for(int i = head[x] ; i ; i = Ed[i].upEd) if(Ed[i].end != p) dfs(Ed[i].end , x);} int fa[_] , ch[_][2] , val[_] , mx[_]; multiset < int > vir[_]; bool nroot(int x){return ch[fa[x]][0] == x || ch[fa[x]][1] == x;}
bool son(int x){return ch[fa[x]][1] == x;}
void up(int x){mx[x] = max(max(val[x] , vir[x].size() ? *--vir[x].end() : (int)-2e9) , max(mx[ch[x][0]] , mx[ch[x][1]]));} void rot(int x){
bool f = son(x); int y = fa[x] , z = fa[y] , w = ch[x][f ^ 1];
fa[x] = z; if(nroot(y)) ch[z][son(y)] = x;
fa[y] = x; ch[x][f ^ 1] = y;
ch[y][f] = w; if(w) fa[w] = y;
up(y);
} void splay(int x){while(nroot(x)){if(nroot(fa[x])) rot(son(x) == son(fa[x]) ? fa[x] : x); rot(x);} up(x);} void access(int x){
for(int y = 0 ; x ; y = x , x = fa[x]){
splay(x); if(ch[x][1]) vir[x].insert(mx[ch[x][1]]);
if(y) vir[x].erase(vir[x].find(mx[y]));
ch[x][1] = y; up(x);
}
} int fdrt(int x){access(x); splay(x); while(ch[x][0]) x = ch[x][0]; splay(x); return x;}
void link(int x){splay(x); int t = pre[x]; access(t); splay(t); fa[x] = t; vir[t].insert(mx[x]); up(t);}
void cut(int x){access(x); splay(x); ch[x][0] = fa[ch[x][0]] = 0; up(x);}
void mdy(int x , int w){access(x); splay(x); val[x] = w; up(x);}
int qry(int x){access(x); splay(x); x = fdrt(x); return mx[ch[x][1]];} int main(){
mx[0] = -2e9; N = read();
for(int i = 1 ; i < N ; ++i){int a = read() , b = read(); addEd(a , b); addEd(b , a);}
dfs(1 , 0); pre[1] = ++N;
for(int i = 1 ; i < N ; ++i) col[i] = read();
for(int i = 1 ; i < N ; ++i) val[i] = val[i + N] = mx[i] = mx[i + N] = read();
for(int i = 1 ; i < N ; ++i) pre[i + N] = pre[i] + N;
for(int i = 1 ; i < N ; ++i) link(i + col[i] * N);
for(int M = read() ; M ; --M){
int tp = read() , u = read();
if(tp == 0) printf("%d\n" , qry(u + col[u] * N));
else if(tp == 1){cut(u + col[u] * N); link(u + (col[u] ^= 1) * N);}
else{int w = read(); mdy(u , w); mdy(u + N , w);}
}
return 0;
}

SPOJ Qtree系列的更多相关文章

  1. SPOJ QTREE 系列解题报告

    题目一 : SPOJ 375 Query On a Tree http://www.spoj.com/problems/QTREE/ 给一个树,求a,b路径上最大边权,或者修改a,b边权为t. #in ...

  2. SPOJ Qtree系列 5/7

    Qtree1 树剖裸题 注意把边权移到深度较深的点上,树剖跳的时候不要将LCA的答案统计上就行了 #include<stdio.h> #include<string.h> #d ...

  3. QTREE 树链剖分---模板 spoj QTREE

    <树链剖分及其应用> 一文讲得非常清楚,我一早上就把他学会了并且A了这题的入门题. spoj QTREE 题目: 给出一棵树,有两种操作: 1.修改一条边的边权. 2.询问节点a到b的最大 ...

  4. SPOJ GSS系列

    众所周知的仅次于ynoi的毒瘤数据结构系列.(跟Qtree系列并列?) GSS1: 长度为 $n$ 的序列 $a$,$m$ 个询问,每次询问区间 $[l,r]$ 之间的最大子段和. $1\le n,m ...

  5. 激!QTREE系列

    我现在才开始刷 QTREE 是不是太弱了?算了不管他…… QTREE: 树链剖分裸题(据说 lct 会超时……该说是真不愧有 spoj 的气息吗?) #include <cstdio> # ...

  6. QTREE系列题解

    打了快一星期的qtree终于打完了- - (其实还有两题改不出来弃疗了QAQ) orz神AK一星期前就虐完QTREE 避免忘记还是简单写下题解吧0 0 QTREE1 题意: 给出一颗带边权树 一个操作 ...

  7. SPOJ QTREE Query on a tree 树链剖分+线段树

    题目链接:http://www.spoj.com/problems/QTREE/en/ QTREE - Query on a tree #tree You are given a tree (an a ...

  8. QTREE系列题目总结

    $QTREE$ 就是一套树上数据结构练习题. 这套题貌似来源于 $SPOJ$,我是在 $luogu$ 看到的. $QTREE1$ 题意 一棵 $n$ 个点的带边权树,要求支持 单边改权值 和 询问路径 ...

  9. spoj GSS系列简要题解

    文章目录 GSS1 GSS2 GSS3 GSS4 GSS5 GSS6 GSS7 GSS8 传送门 这个GSSGSSGSS系列全部是跟子段有关的数据结构菜题. 于是来水一篇博客. GSS1 传送门 题意 ...

随机推荐

  1. 在交互环境下使用 Pyspark 提交任务给 Spark 解决 : java.sql.SQLException: No suitable driver

    在 jupyter 上启用 local 交互环境和 spark 进行交互使用 imapla 来帮助 spark 取数据却失败了 from pyspark.sql import SparkSession ...

  2. Centos 或者 Redhat修改系统时间

    使用Redhat,打开电脑,系统时间比本地时间快一个小时 直接敲命令:date -s "2019-4-2 09:14:00"是立即生效了,但是重启后,系统时间还是原来的. 修改了其 ...

  3. 【BigData】Java基础_终端输入2个数字并求和

    1.需求描述 在终端输入2个数字,然后根据输入的数字求和 2.实现代码 package cn.test.logan; import java.util.Scanner; public class Te ...

  4. UDF——已知入口压力和流量计算压降

    有时候我们在计算内流,比如管道内的流动时,只知道入口压力和流量,而我们想要计算得到出口的压力,这个应该怎么办呢?当然新版本的Fluent已经自带了流量出口边界,而这里我们采用Fluent的UDF来实现 ...

  5. ruby-on-rails-BUG

    Ruby on Rails errors: (新手期) rails 自动化部署教程 Q1: rvm 无法使用 $ rvm use 1.9.3 --default RVM is not a functi ...

  6. IIS连接数、并发连接数、最大并发工作线程数、应用程序池的队列长度、应用程序池的最大工作进程数详解

    IIS:连接数.并发连接数.最大并发工作线程数.应用程序池的队列长度.应用程序池的最大工作进程数详解 iis性能指标的各种概念:连接数.并发连接数.最大并发工作线程数.应用程序池的队列长度.应用程序池 ...

  7. XML读写工具

    import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import j ...

  8. SVM – 核函数

    核函数的起源是对于线性不可分的分类情况,其实可以通过p次方多项式,及非线性模型进行分类:然后对于这类非线性多次方的,其实可以按照广义线性模型来进行升维变形,使之成为线性模型,这样就可以放到SVM中来进 ...

  9. windows zip命令

    针对 Windows 用户的提示: win不自带zip,因此 Windows 用户可以从 GnuWin32 项目页面 上下载zip ,并将 C:\Program Files\GnuWin32\bin( ...

  10. PAT 甲级 1020 Tree Traversals (25分)(后序中序链表建树,求层序)***重点复习

    1020 Tree Traversals (25分)   Suppose that all the keys in a binary tree are distinct positive intege ...