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("\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;
}

转载于:https://www.cnblogs.com/Itst/p/11341085.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
洛谷的SPOJ需要注册一个SPOJ账号并进行绑定才能进行交题。您可以按照以下步骤进行注册: 1. 打开洛谷网站(https://www.luogu.com.cn/)并登录您的洛谷账号。 2. 在网站顶部导航栏中找到“题库”选项,将鼠标悬停在上面,然后选择“SPOJ”。 3. 在SPOJ页面上,您会看到一个提示,要求您注册SPOJ账号并进行绑定。点击提示中的链接,将会跳转到SPOJ注册页面。 4. 在SPOJ注册页面上,按照要求填写您的用户名、密码和邮箱等信息,并完成注册。 5. 注册完成后,返回洛谷网站,再次进入SPOJ页面。您会看到一个输入框,要求您输入刚刚注册的SPOJ用户名。输入用户名后,点击“绑定”按钮即可完成绑定。 现在您已经成功注册并绑定了SPOJ账号,可以开始在洛谷的SPOJ题库上刷题了。祝您顺利完成编程练习!\[1\]\[2\] #### 引用[.reference_title] - *1* *3* [(洛谷入门系列,适合洛谷新用户)洛谷功能全解](https://blog.csdn.net/rrc12345/article/details/122500057)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [luogu p7492 序列](https://blog.csdn.net/zhu_yin233/article/details/122051384)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值