SPOJ QTREE

Posted forever97‘s blog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SPOJ QTREE相关的知识,希望对你有一定的参考价值。

QTREE

/*
    题目大意:维护一棵树,允许修改边权以及查询链上最大值
    题解:我们将边权转为点权,标记在深度较深的点上,树链剖分后用线段树处理即可
*/
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=20010;
int top[N],nxt[N],v[N],g[N],d[N],son[N],f[N],size[N],st[N],en[N],dfn,ed;
void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
void init(){ 
    memset(g,dfn=ed=0,sizeof(g));
    memset(v,0,sizeof(v));
    memset(nxt,0,sizeof(nxt));
    memset(son,-1,sizeof(son));
}
void dfs(int x){
    size[x]=1;
    for(int i=g[x];i;i=nxt[i])if(v[i]!=f[x]){
        f[v[i]]=x,d[v[i]]=d[x]+1;
        dfs(v[i]),size[x]+=size[v[i]];
        if(size[v[i]]>size[son[x]])son[x]=v[i];
    }
}
void dfs2(int x,int y){
    st[x]=++dfn;top[x]=y;
    if(son[x])dfs2(son[x],y);
    for(int i=g[x];i;i=nxt[i])if(v[i]!=son[x]&&v[i]!=f[x])dfs2(v[i],v[i]);
    en[x]=dfn;
}
struct Node{int l,r;int Max;}Tree[N*3];
void build(int x,int l,int r){
    Tree[x].l=l;Tree[x].r=r;Tree[x].Max=0;
    if(l==r)return;int mid=(l+r)>>1;
    build(x<<1,l,mid);build((x<<1)|1,mid+1,r);
}
void up(int x){Tree[x].Max=max(Tree[x<<1].Max,Tree[(x<<1)|1].Max);}
void update(int x,int k,int val){
    if(Tree[x].l==k&&Tree[x].r==k){Tree[x].Max=val;return;}
    int mid=(Tree[x].l+Tree[x].r)>>1;
    if(k<=mid)update(x<<1,k,val);
    else update((x<<1)|1,k,val);
    up(x);
}
int query(int x,int l,int r){
    if(Tree[x].l==l&&Tree[x].r==r)return Tree[x].Max;
    int mid=(Tree[x].l+Tree[x].r)>>1;
    if(r<=mid)return query(x<<1,l,r);
    else if(l>mid)return query((x<<1)|1,l,r);
    else return max(query(x<<1,l,mid),query((x<<1)|1,mid+1,r));
}
int find(int x,int y){
    int tmp=0;
    for(;top[x]!=top[y];x=f[top[x]]){
        if(d[top[x]]<d[top[y]]){int z=x;x=y;y=z;}
        tmp=max(tmp,query(1,st[top[x]],st[x]));
    }if(x==y)return tmp;
    if(d[x]>d[y]){int z=x;x=y;y=z;}
    return max(tmp,query(1,st[son[x]],st[y]));
}int e[N][3];
int main(int n,int T){
    scanf("%d",&T);
    while(T--){
        init(); scanf("%d",&n);
        for(int i=0;i<n-1;i++){
            scanf("%d%d%d",&e[i][0],&e[i][1],&e[i][2]);
            add(e[i][0],e[i][1]);
            add(e[i][1],e[i][0]);
        }dfs(1);dfs2(1,1);
        build(1,1,dfn);
        for(int i=0;i<n-1;i++){
            if(d[e[i][0]]>d[e[i][1]])swap(e[i][0],e[i][1]);
            update(1,st[e[i][1]],e[i][2]);
        }char op[10]; int u,w;
        while(scanf("%s",op)==1){
            if(op[0]==‘D‘)break;
            scanf("%d%d",&u,&w);
            if(op[0]==‘Q‘)printf("%d\n",find(u,w));
            else update(1,st[e[u-1][1]],w);
        }
    }return 0;
}

QTREE2

/*
    题目大意:给出一棵边权树,要求实现树链求和以及求链上第k个元素
    题解:边权转点权后用LCT维护,注意求Qkth和sum时区别于点权树的讨论
*/
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
const int N=300010;
namespace Link_Cut_Tree{
    vector<int> v[N],w[N];
    int f[N],son[N][2],tmp[N],size[N],val[N],sum[N]; 
    void init(){
        memset(val,0,sizeof(val));
        memset(sum,0,sizeof(sum));
        memset(f,0,sizeof(f));
        memset(son,0,sizeof(son));
        for(int i=1;i<N;i++)size[i]=1,v[i].clear(),w[i].clear();
    }
    bool isroot(int x){return !f[x]||son[f[x]][0]!=x&&son[f[x]][1]!=x;}
    void up(int x){ 
        sum[x]=val[x]+sum[son[x][0]]+sum[son[x][1]];
        size[x]=size[son[x][0]]+size[son[x][1]]+1;
    }
    void rotate(int x){
        int y=f[x],w=son[y][1]==x;
        son[y][w]=son[x][w^1];
        if(son[x][w^1])f[son[x][w^1]]=y;
        if(f[y]){
            int z=f[y];
            if(son[z][0]==y)son[z][0]=x;else if(son[z][1]==y)son[z][1]=x;
        }f[x]=f[y];f[y]=x;son[x][w^1]=y;up(y);
    }
    void splay(int x){
        int s=1,i=x,y;tmp[1]=i;
        while(!isroot(i))tmp[++s]=i=f[i];
        while(!isroot(x)){
            y=f[x]; 
            if(!isroot(y)){if((son[f[y]][0]==y)^(son[y][0]==x))rotate(x);else rotate(y);}
            rotate(x);
        }up(x);
    }
    void access(int x){for(int y=0;x;y=x,x=f[x])splay(x),son[x][1]=y,up(x);}
    // 求边权树的链和
    int ask(int x,int y){  
        access(y),y=0;  
        int ans=0;  
        while(x){  
            splay(x);  
            if(!f[x])return sum[son[x][1]]+sum[y];  
        	son[x][1]=y; up(x);  
            x=f[y=x];  
        }  
    }  
    void dfs(int x,int fx){
        for(int i=0;i<v[x].size();i++){
            int y=v[x][i];
            if(y==fx)continue;
            f[y]=x;
            val[y]=w[x][i];
            dfs(y,x);
        }up(x);
    }
    int kth(int x,int k){
        while(size[son[x][0]]+1!=k){
            if(k<size[son[x][0]]+1)x=son[x][0];
            else{
                k-=size[son[x][0]]+1;
                x=son[x][1];
            }
        }return x;
    }
    // 求边权树u->v的第k个点
    int Qkth(int u,int v,int k){
        access(u),u=0;
        while(v){
            splay(v);
            if(!f[v]){  
                if(size[son[v][1]]+1==k)return v;  
                else if(size[son[v][1]]+1>k)return kth(son[v][1],size[son[v][1]]+1-k);  
                else return kth(u,k-(size[son[v][1]]+1));  
            }son[v][1]=u;
            up(v); v=f[u=v]; 
        }
    }
}
using namespace Link_Cut_Tree;
int T,x,y,z,k,n;
char op[10];
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);  
        init();  
        for(int i=1;i<n;i++){  
            scanf("%d%d%d",&x,&y,&z);  
              v[x].push_back(y);
              w[x].push_back(z);
              v[y].push_back(x);
              w[y].push_back(z);
        }dfs(1,0);  
        while(scanf("%s",op)){  
            if(op[1]==‘O‘)break;  
            if(op[0]==‘D‘){  
                scanf("%d%d",&x,&y);  
                printf("%d\n",ask(x,y));  
            }  
            else{    
                scanf("%d%d%d",&x,&y,&k);  
                printf("%d\n",Qkth(x,y,k));  
            }  
        }  
    }return 0;
}

QTREE3

/*
    题目大意:给出一棵一开始都是白点的树,要求维护操作:
        1.将某个点的颜色取反(黑白互换)
        2.查询1到x的路径上第一个黑色的点
    题解:LCT维护子树中黑点的数量,对于颜色取反,则直接flap,
    对于查询操作通过access(x)将1到x这条链取出,splay上查找即可
    注意是从1到x,所以是makeroot(1),保证1节点的键值最小
*/
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=100010;
namespace Link_Cut_Tree{
    int f[N],son[N][2],val[N],sum[N],tmp[N];bool rev[N];
    void init(){
        memset(f,0,sizeof(f));
        memset(son,0,sizeof(son));
        memset(val,0,sizeof(val));
        memset(rev,0,sizeof(rev));
        memset(sum,0,sizeof(sum));
    } 
    bool isroot(int x){return !f[x]||son[f[x]][0]!=x&&son[f[x]][1]!=x;}
    void rev1(int x){if(!x)return;swap(son[x][0],son[x][1]);rev[x]^=1;}
    void pb(int x){if(rev[x])rev1(son[x][0]),rev1(son[x][1]),rev[x]=0;}
    void up(int x){sum[x]=sum[son[x][0]]+sum[son[x][1]]+val[x];}
    void rotate(int x){
        int y=f[x],w=son[y][1]==x;
        son[y][w]=son[x][w^1];
        if(son[x][w^1])f[son[x][w^1]]=y;
        if(f[y]){
            int z=f[y];
            if(son[z][0]==y)son[z][0]=x;else if(son[z][1]==y)son[z][1]=x;
        }f[x]=f[y];f[y]=x;son[x][w^1]=y;up(y);
    }
    void splay(int x){
        int s=1,i=x,y;tmp[1]=i;
        while(!isroot(i))tmp[++s]=i=f[i];
        while(s)pb(tmp[s--]);
        while(!isroot(x)){
            y=f[x]; 
            if(!isroot(y)){if((son[f[y]][0]==y)^(son[y][0]==x))rotate(x);else rotate(y);}
            rotate(x);
        }up(x);
    }
    void access(int x){for(int y=0;x;y=x,x=f[x])splay(x),son[x][1]=y,up(x);}
    int root(int x){access(x);splay(x);while(son[x][0])x=son[x][0];return x;}
    void makeroot(int x){access(x);splay(x);rev1(x);}
    void link(int x,int y){makeroot(x);f[x]=y;access(x);}
    int flip(int x){makeroot(x);val[x]^=1;up(x);}
    // 查询从1->x的第一个黑点
    int ask(int x){
        makeroot(1); 
        access(x); splay(x); // splay只保证了x到根节点标记的下传
        if(!sum[x])return -1;
        while(x){
            pb(x); // 因为有makeroot操作,所以需要下传标记
            if(!sum[son[x][0]]&&val[x]==1)return x;
            else if(sum[son[x][0]])x=son[x][0];
            else x=son[x][1];
        }
    }
}
using namespace Link_Cut_Tree;
int n,q,x,y;
int main(){
    while(~scanf("%d%d",&n,&q)){
        init();
        for(int i=1;i<n;i++){
            scanf("%d%d",&x,&y);
            link(x,y);
        }
        while(q--){
            scanf("%d%d",&x,&y);
            if(x)printf("%d\n",ask(y));
            else flip(y);
        }
    }return 0;
}

QTREE4

/*
    题目大意:给出一棵边权树,一开始所有点都是白点,
    要求维护两种操作:
        1.颜色取反(黑白互变)
        2.查询树上最远的两个白点之间的距离
    题解:我们维护重心树,对于每个重心维护分治层所有白点到其距离,
    将其保存在对应方向子重心的优先队列中,
    重心处另外维护一个队列保存该分治层每个子重心所产生的最大答案,即最大值加次大值,
    那么每个重心处产生的最大答案的最值就是答案,
    考虑修改问题,等价于在处理优先队列的删除问题,
    对于每个需要删除操作的优先队列,我们额外创建一个优先队列将删除元素加入其中,
    当两个队列top元素相同时同时删去即可。
*/
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
const int N=200010;
const int INF=0x3f3f3f3f;
int tot,g[N],nxt[N<<1],v[N<<1],w[N<<1];
int TOT,G[N],NXT[N*20],V[N*20],W[N*20];
void init(){
    memset(g,-1,sizeof(g));tot=0;
    memset(G,-1,sizeof(G));TOT=0;
}
void add_edge(int x,int y,int z){v[tot]=y,w[tot]=z,nxt[tot]=g[x],g[x]=tot++;}
void ADD_EDGE(int x,int y,int z){V[TOT]=y,W[TOT]=z,NXT[TOT]=G[x],G[x]=TOT++;}
int sum,root,size[N],dp[N],vis[N];
void getroot(int x,int fx){
    size[x]=1; dp[x]=0;
    for(int i=g[x];~i;i=nxt[i]){
        if(!vis[v[i]]&&v[i]!=fx){
            getroot(v[i],x);
            size[x]+=size[v[i]];
            dp[x]=max(dp[x],size[v[i]]);
        }
    }dp[x]=max(dp[x],sum-size[x]);
    if(dp[x]<dp[root])root=x;
}
struct Que{
    priority_queue<int> Q,D;
    void clear(){
        while(!Q.empty())Q.pop();
        while(!D.empty())D.pop();
    }
    void add(int x){Q.push(x);}
    void del(int x){D.push(x);}
    int top(){
        for(;;){
            if(Q.empty())return -INF;
            else if(!D.empty()&&Q.top()==D.top())Q.pop(),D.pop();
            else return Q.top();
        }
    }
    int toptwo(){
        int x=top();
        del(x);
        int y=top();
        add(x);
        if(y==-INF)return x==-INF?x:0;
        return max(x+y,0);
    }
}P[N],Q[N],ans;
int dis[N],col[N];
void getdis(int rt,int x,int fx){
    Q[rt].add(dis[x]);
    for(int i=g[x];~i;i=nxt[i]){
        if(v[i]==fx||vis[v[i]])continue;
        dis[v[i]]=dis[x]+w[i];
        getdis(rt,v[i],x);
    }
}
void getdeep(int rt,int x,int fx){
    ADD_EDGE(x,rt,dis[x]);
    for(int i=g[x];~i;i=nxt[i]){
        if(v[i]==fx||vis[v[i]])continue;
        dis[v[i]]=dis[x]+w[i];
        getdeep(rt,v[i],x);
    }
}
int belong[N];
void build(int x,int fx,int from,int d){
    belong[x]=fx; 
    dis[x]=0; getdeep(x,x,fx); P[x].add(0);
    if(fx){
        dis[from]=d;
        getdis(x,from,fx);
        P[fx].add(Q[x].top());
    }
    vis[x]=1;
    for(int i=g[x];~i;i=nxt[i])if(!vis[v[i]]){
        root=0; sum=size[v[i]];
        getroot(v[i],x);
        build(root,x,v[i],w[i]);
    }
    ans.add(P[x].toptwo());
}
void change(int rt){
    int a=P[rt].toptwo();
    if(col[rt])P[rt].add(0);
    else P[rt].del(0);
    int b=P[rt].toptwo();
    if(a!=b)ans.del(a),ans.add(b);
    int x=rt;
    for(int i=G[rt];~NXT[i];i=NXT[i]){
        int y=V[NXT[i]],len=W[NXT[i]];
        x=V[i];
        a=Q[x].top();
        if(col[rt])Q[x].add(len);
        else Q[x].del(len);
        b=Q[x].top();
        if(a!=b){
            int c=P[y].toptwo();
            P[y].del(a),P[y].add(b);
            int d=P[y].toptwo();
            if(c!=d)ans.del(c),ans.add(d);
        }
    }
}
int n,m,a,b,c,x;
char op[10];
int main(){
    scanf("%d",&n); init();
    for(int i=1;i<=n;i++)col[i]=1;
    for(int i=1;i<n;i++){
        scanf("%d%d%d",&a,&b,&c);
        add_edge(a,b,c);
        add_edge(b,a,c);
    }
    memset(vis,0,sizeof(vis));
    dp[root=0]=sum=n;
    getroot(1,0);
    build(root,0,0,0);
    scanf("%d",&m);
    int cnt=n;
    while(m--){
        scanf("%s",op);
        if(op[0]==‘A‘){
            if(cnt)printf("%d\n",ans.top());
            else puts("They have disappeared.");
        }else{
            scanf("%d",&x);
            col[x]^=1;
            if(col[x])cnt++;
            else cnt--;
            change(x);
        }
    }return 0;
}

QTREE5

/*
    题目大意:给出一棵树,边权均为1,一开始所有点都是白点,
    要求维护两种操作:
        1.颜色取反(黑白互变)
        2.查询树上某点距离最近的白点与其距离
    题解:我们维护重心树,对于每个重心维护分治层所有白点到其距离,
    将其保存在对应方向子重心的优先队列中,
    重心处另外维护一个队列表示每个子重心的最小答案,
    考虑修改问题,等价于在处理优先队列的删除问题,
    对于每个需要删除操作的优先队列,我们额外创建一个优先队列将删除元素加入其中,
    当两个队列top元素相同时同时删去即可。
    对于查询操作,我们沿着重心链用经过每个重心的最优值更新答案
*/
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
const int N=200010;
const int INF=0x3f3f3f3f;
int tot,g[N],nxt[N<<1],v[N<<1],w[N<<1];
int TOT,G[N],NXT[N*20],V[N*20],W[N*20];
void init(){
    memset(g,-1,sizeof(g));tot=0;
    memset(G,-1,sizeof(G));TOT=0;
}
void add_edge(int x,int y,int z){v[tot]=y,w[tot]=z,nxt[tot]=g[x],g[x]=tot++;}
void ADD_EDGE(int x,int y,int z){V[TOT]=y,W[TOT]=z,NXT[TOT]=G[x],G[x]=TOT++;}
int sum,root,size[N],dp[N],vis[N];
void getroot(int x,int fx){
    size[x]=1; dp[x]=0;
    for(int i=g[x];~i;i=nxt[i]){
        if(!vis[v[i]]&&v[i]!=fx){
            getroot(v[i],x);
            size[x]+=size[v[i]];
            dp[x]=max(dp[x],size[v[i]]);
        }
    }dp[x]=max(dp[x],sum-size[x]);
    if(dp[x]<dp[root])root=x;
}
struct Que{
    priority_queue<int,vector<int>,greater<int> > Q,D;
    void clear(){
        while(!Q.empty())Q.pop();
        while(!D.empty())D.pop();
    }
    void add(int x){Q.push(x);}
    void del(int x){D.push(x);}
    int top(){
        for(;;){
            if(Q.empty())return INF;
            else if(!D.empty()&&Q.top()==D.top())Q.pop(),D.pop();
            else return Q.top();
        }
    }
}P[N],Q[N];
int dis[N],col[N];
void getdis(int rt,int x,int fx){
    for(int i=g[x];~i;i=nxt[i]){
        if(v[i]==fx||vis[v[i]])continue;
        dis[v[i]]=dis[x]+w[i];
        getdis(rt,v[i],x);
    }
}
void getdeep(int rt,int x,int fx){
    ADD_EDGE(x,rt,dis[x]);
    for(int i=g[x];~i;i=nxt[i]){
        if(v[i]==fx||vis[v[i]])continue;
        dis[v[i]]=dis[x]+w[i];
        getdeep(rt,v[i],x);
    }
}
int belong[N];
void build(int x,int fx,int from,int d){
    belong[x]=fx; 
    dis[x]=0; getdeep(x,x,fx); 
    if(fx){
        dis[from]=d;
        getdis(x,from,fx);
    }
    vis[x]=1;
    for(int i=g[x];~i;i=nxt[i])if(!vis[v[i]]){
        root=0; sum=size[v[i]];
        getroot(v[i],x);
        build(root,x,v[i],w[i]);
    }
}
void change(int rt){
    if(col[rt])P[rt].add(0);
    else P[rt].del(0);
    int x=rt;
    for(int i=G[rt];~NXT[i];i=NXT[i]){
        int y=V[NXT[i]],len=W[NXT[i]];
        x=V[i];
        int a=Q[x].top();
        if(col[rt])Q[x].add(len);
        else Q[x].del(len);
        int b=Q[x].top();
        if(a!=b)P[y].del(a),P[y].add(b);
    }
}
int ask(int rt){
    int ans=INF;
    for(int i=G[rt];~i;i=NXT[i]){
        int x=V[i],len=W[i];
        int a=P[x].top();
        if(a+len<ans)ans=a+len;
    }
    return ans;
}
int n,m,a,b,c,x,op;
int main(){
    scanf("%d",&n); init();
    for(int i=1;i<=n;i++)col[i]=0;
    for(int i=1;i<n;i++){
        scanf("%d%d",&a,&b);
        add_edge(a,b,1);
        add_edge(b,a,1);
    }
    memset(vis,0,sizeof(vis));
    dp[root=0]=sum=n;
    getroot(1,0);
    build(root,0,0,0);
    scanf("%d",&m);
    int cnt=0;
    while(m--){
        scanf("%d%d",&op,&x);
        if(op==1){
            if(cnt)printf("%d\n",ask(x));
            else puts("-1");
        }else{
            col[x]^=1;
            if(col[x])cnt++;
            else cnt--;
            change(x);
        }
    }return 0;
}

QTREE6

/*
    题目大意:给出一棵树,一开始树上点均为黑色,要求维护操作
        1.翻转某点的颜色(黑白互换),
        2.查询某个点所在连通块(颜色相同则连通)大小
    题解:我们分别维护黑点LCT和白点LCT,当一个点从黑变白时,
    将其从黑点LCT中与父节点断开,然后在白点LCT中与父节点相连,
    这样我们就保证了每个连通块在LCT中只有父节点是不同色的而其余一定是连通的。
    考虑用LCT维护子树信息,根据在LCT上的连接情况,我们将点的儿子分为实儿子和虚儿子
    实儿子是原本树结构上相连的点,实儿子的子树为实子树,虚儿子的子树为虚子树
    x的LCT子树的信息和等于x的实儿子的LCT子树信息和加上x的虚子树的信息和加上x自己
    在进行access操作时,我们会有更换一个点的x右儿子的操作,
    这时我们要把x原来的右儿子的LCT子树信息加入x的虚子树信息,
    把x的新的右儿子的LCT子树信息从x的虚子树信息中减去
    同时在link x到y的时候,我们需要对y进行access再splay
    这样就只会对y的虚子树信息和LCT子树信息产生影响,而不会影响到y的祖先节点
*/
#include <cstdio>
#include <algorithm>
#include <set>
const int N=100010; 
using namespace std;
int n,m,i,k,x,y,c[N],fa[N],g[N],v[N<<1],nxt[N<<1],ed;
struct LCT{
    int son[N][2],f[N],sum[N],s[N];
    bool isroot(int x){return !f[x]||son[f[x]][0]!=x&&son[f[x]][1]!=x;}
    void up(int x){sum[x]=1+sum[son[x][0]]+sum[son[x][1]]+s[x];}
    void rotate(int x){
        int y=f[x],w=son[y][1]==x;
        son[y][w]=son[x][w^1];
        if(son[x][w^1])f[son[x][w^1]]=y;
        if(f[y]){
            int z=f[y];
            if(son[z][0]==y)son[z][0]=x;
            if(son[z][1]==y)son[z][1]=x;
        }f[x]=f[y];f[y]=x;son[x][w^1]=y;up(y);
    }
    void splay(int x){
        while(!isroot(x)){
            int y=f[x];
            if(!isroot(y)){
                if((son[f[y]][0]==y)^(son[y][0]==x))rotate(x);
                else rotate(y);
            }rotate(x);
        }up(x);
    }
    void access(int x){
        for(int y=0;x;y=x,x=f[x]){
            splay(x);
            if(son[x][1])s[x]+=sum[son[x][1]];
            if(son[x][1]=y)s[x]-=sum[y];
            up(x);
        }
    }
    int root(int x){access(x);splay(x);while(son[x][0])x=son[x][0];return x;}
    void link(int x){access(fa[x]);splay(fa[x]);splay(x);son[fa[x]][1]=x;up(f[x]=fa[x]);}
    void cut(int x){access(x);splay(x);f[son[x][0]]=0;son[x][0]=0;up(x);}
    int ask(int x){splay(x=root(x));return sum[son[x][1]];}
}T[2];
void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
void dfs(int x){
    for(int i=g[x];i;i=nxt[i])if(v[i]!=fa[x]){
        fa[v[i]]=T[c[v[i]]].f[v[i]]=x;
        dfs(v[i]);
        T[c[v[i]]].s[x]+=T[c[v[i]]].sum[v[i]];
    }T[0].up(x),T[1].up(x);
}
void read(int&a){
    char c;
    while(!(((c=getchar())>=‘0‘)&&(c<=‘9‘)));a=c-‘0‘;
    while(((c=getchar())>=‘0‘)&&(c<=‘9‘))(a*=10)+=c-‘0‘;
}
int main(){
     read(n);
     for(i=1;i<n;i++)read(x),read(y),add(x,y),add(y,x);
     for(i=1;i<=n;i++)c[i]=0;
     fa[1]=T[c[1]].f[1]=n+1;dfs(1);
     T[c[1]].s[n+1]+=T[c[1]].sum[1];read(m);
     while(m--){
         read(k);read(x);
         if(!k)printf("%d\n",T[c[x]].ask(x));
         else T[c[x]].cut(x),T[c[x]^=1].link(x);
     }return 0;
}

QTREE7

/*
    题目大意:给出一棵树,树上每个点为黑色或者白色,每个点有个点权,
    我们认为树上颜色相同并通过边连接的块为连通块
    要求维护操作:
        0.查询x所在连通块中权值最大的点
        1.翻转某点的颜色(黑白互换)
        2.改变某点的权值
    题解:我们分别维护黑点LCT和白点LCT,当一个点从黑变白时,
    将其从黑点LCT中与父节点断开,然后在白点LCT中与父节点相连,
    这样我们就保证了每个连通块在LCT中只有父节点是不同色的而其余一定是连通的。
    考虑用LCT维护子树信息,根据在LCT上的连接情况,我们将点的儿子分为实儿子和虚儿子
    实儿子是原本树结构上相连的点,实儿子的子树为实子树,虚儿子的子树为虚子树
    x的LCT子树的信息和等于x的实儿子的LCT子树信息和加上x的虚子树的信息和加上x自己
    在进行access操作时,我们会有更换一个点的x右儿子的操作,
    这时我们要把x原来的右儿子的LCT子树信息加入x的虚子树信息,
    把x的新的右儿子的LCT子树信息从x的虚子树信息中减去
    同时在link x到y的时候,我们需要对y进行access再splay
    这样就只会对y的虚子树信息和LCT子树信息产生影响,而不会影响到y的祖先节点
    因为要维护的信息是极值,因此我们要用multiset来维护虚子树信息
*/
#include <cstdio>
#include <algorithm>
#include <ctype.h>
#include <set>
const int N=100010; 
using namespace std;
int n,m,k,x,y,c[N],fa[N],g[N],v[N<<1],nxt[N<<1],ed;
struct LCT{
    int son[N][2],f[N],val[N],mx[N];
    multiset<int> s[N];
    bool isroot(int x){return !f[x]||son[f[x]][0]!=x&&son[f[x]][1]!=x;}
    void up(int x){
        mx[x]=val[x];
        if(s[x].size())mx[x]=max(mx[x],*s[x].rbegin());
        if(son[x][0])mx[x]=max(mx[x],mx[son[x][0]]);
        if(son[x][1])mx[x]=max(mx[x],mx[son[x][1]]);
    }
    void rotate(int x){
        int y=f[x],w=son[y][1]==x;
        son[y][w]=son[x][w^1];
        if(son[x][w^1])f[son[x][w^1]]=y;
        if(f[y]){
            int z=f[y];
            if(son[z][0]==y)son[z][0]=x;
            if(son[z][1]==y)son[z][1]=x;
        }f[x]=f[y];f[y]=x;son[x][w^1]=y;up(y);
    }
    void splay(int x){
        while(!isroot(x)){
            int y=f[x];
            if(!isroot(y)){
                if((son[f[y]][0]==y)^(son[y][0]==x))rotate(x);
                else rotate(y);
            }rotate(x);
        }up(x);
    }
    void access(int x){
        for(int y=0;x;y=x,x=f[x]){
            splay(x);
            if(son[x][1])s[x].insert(mx[son[x][1]]);
            if(son[x][1]=y)s[x].erase(s[x].find(mx[y]));
            up(x);
        }
    }
    int root(int x){access(x);splay(x);while(son[x][0])x=son[x][0];return x;}
    void link(int x){access(fa[x]);splay(fa[x]);splay(x);son[fa[x]][1]=x;up(f[x]=fa[x]);}
    void cut(int x){access(x);splay(x);f[son[x][0]]=0;son[x][0]=0;up(x);}
    int ask(int x){
		int t=c[x]; splay(x=root(x));
		return t==c[x]?mx[x]:mx[son[x][1]];
	}
    void modify(int x,int v){access(x),splay(x),val[x]=v,up(x);}  
}T[2];
void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
void dfs(int x){
    for(int i=g[x];i;i=nxt[i])if(v[i]!=fa[x]){
        fa[v[i]]=T[c[v[i]]].f[v[i]]=x;
        dfs(v[i]);
        T[c[v[i]]].s[x].insert(T[c[v[i]]].mx[v[i]]);
    }T[0].up(x),T[1].up(x);
}
void read(int &x){  
    char ch;
    for(ch=getchar();!isdigit(ch)&&ch!=‘-‘;ch=getchar());  
    int t;if(ch==‘-‘)t=-1,ch=getchar();else t=1;  
    for(x=0;isdigit(ch);ch=getchar())x=x*10+ch-‘0‘;  
    x=x*t;  
}  
int main(){
     read(n);
     for(int i=1;i<n;i++)read(x),read(y),add(x,y),add(y,x);
     for(int i=1;i<=n;i++)read(c[i]);  
     for(int i=1;i<=n;i++)read(T[0].val[i]),T[1].val[i]=T[0].val[i];  
     dfs(1); read(m);
     while(m--){
         read(k);read(x);
         if(k==0)printf("%d\n",T[c[x]].ask(x));
         else if(k==1)T[c[x]].cut(x),T[c[x]^=1].link(x);
         else if(k==2)read(y),T[0].modify(x,y),T[1].modify(x,y);
     }return 0;
}

以上是关于SPOJ QTREE的主要内容,如果未能解决你的问题,请参考以下文章

SPOJ QTREE 树链剖分

SPOJ QTREE Query on a tree

SPOJ QTREE Query on a tree ——树链剖分 线段树

SPOJ QTREE

Spoj375 Qtree

Spoj375 Qtree--树链剖分