SPOJ Qtree系列

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("
" , 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
" , LCA(read() , read()).second);
    				break;
    			case 'T':
    				a = read();
    				b = read();
    				c = read();
    				printf("%d
" , 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('
');
		}
	}
	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
" , 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
" , 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
" , 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;
}
原文地址:https://www.cnblogs.com/Itst/p/11341085.html