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. Spark 部署即提交模式意义解析

    Spark 的官方从 Cluster Mode Overview 中,官方向我们介绍了 cluster 模式的部署方式. Spark 作为独立进程在集群上运行,他们通过 SparkContext 进行 ...

  2. nginx location配置说明

    nginx location语法规则:location  [=|~|~*|^~]  /uri/  { … } nginx的location匹配的变量是$uri 规则优先级 = 高于 ^~ 高于 ~* ...

  3. linux高性能服务器编程 (四) --TCP/IP通信案例

    第四章 TCP/IP通信案例 HTTP代理服务器的大致工作原理        在HTTP通信链上,客户端和服务器之间通常存在某些中转代理服务器.它们提供对目标资源的中转访问.一个HTTP请求可能被多个 ...

  4. python crawler

    crawl blog website: www.apress.com # -*- coding: utf-8 -*- """ Created on Wed May 10 ...

  5. [技术博客]使用adb命令获取app(游戏)错误和警告日志

    adb命令的使用 直接在命令行中输入: adb logcat *:W 注意:这句命令的意思是显示所有优先级大于等于警告(Warning)的日志,查找崩溃问题一般用: adb logcat *:E 注意 ...

  6. Bert 时代的创新(应用篇):Bert 在 NLP 各领域的

    Bert 时代的创新(应用篇):Bert 在 NLP 各领域的

  7. 七年老运维实战中的 Shell 开发经验总结【转】

    无论是系统运维,还是应用运维,均可分为“纯手工”—> “脚本化”—> “自动化”—>“智能化”几个阶段,其中自动化阶段,主要是将一些重复性人工操作和运维经验封装为程序或脚本,一方面避 ...

  8. 【C++】C++中的异常解析

    异常是程序在执行期间产生的问题.C++ 异常是指在程序运行时发生的特殊情况,比如尝试除以零的操作. 异常提供了一种转移程序控制权的方式.C++ 异常处理涉及到三个关键字:try.catch.throw ...

  9. Xamarin.FormsShell基础教程(8)Shell的模版构成

    Xamarin.FormsShell基础教程(8)Shell的模版构成 Shell模版创建的页面包含内容页.标签栏.侧滑菜单.搜索框四部分.下面依次介绍这几个部分. 内容页:内容页就是为用户呈现内容的 ...

  10. js开启和关闭页面滚动【亲测有效】

    在移动端的页面开发过程中,经常会遇到点击弹框禁止页面滚动的情景,下面就来说下具体的做法... 第一步:构建一个函数 function bodyScroll(event){ event.preventD ...