Qtree系列都跟树有着莫大的联系,这道题当然也不例外

我是题面

读完题,我们大概就知道了,这道题非常简单,可以说是模板题。树剖+线段树轻松解决

直接看代码吧

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cctype>
#define ll long long
#define gc() getchar()
#define maxn 100010
using namespace std;

inline ll read(){     //很朴素的快读,不多解释
    ll a=0;int f=0;char p=gc();
    while(!isdigit(p)){f|=p=='-';p=gc();}
    while(isdigit(p)){a=(a<<3)+(a<<1)+(p^48);p=gc();}
    return f?-a:a;
}
void write(ll a){     //很朴素的快写,也不多解释
    if(a>9)write(a/10);
    putchar(a%10+'0');
}
int n,a[maxn];    //a数组表示点与父亲连边的长度

int tot,head[maxn];
struct ahaha1{     //前向星存边
    int w,to,next;
}e[maxn<<1];
inline void add(int u,int v,int w){
    e[tot].w=w;e[tot].to=v;e[tot].next=head[u];head[u]=tot++;
}

int f[maxn],sz[maxn],dep[maxn],son[maxn];     //f表示点的父亲,sz表示点的子树上节点个数,dep表示节点的深度,son表示子树最大的子节点
void dfs(int u,int fa){
    int maxa=0;sz[u]=1;
    for(int i=head[u];~i;i=e[i].next){
        int v=e[i].to;if(v==fa)continue;
        f[v]=u;dep[v]=dep[u]+1;a[v]=e[i].w;
        dfs(v,u);sz[u]+=sz[v];
        if(sz[v]>maxa)maxa=sz[v],son[u]=v;
    }
}
int b[maxn],in[maxn],top[maxn];    //b表示当前编号所指向的点,in表示点的编号,top表示点所在链的顶端
void dfs(int u,int fa,int topf){
    in[u]=++tot;b[tot]=u;top[u]=topf;
    if(!son[u])return;
    dfs(son[u],u,topf);
    for(int i=head[u];~i;i=e[i].next){
        int v=e[i].to;if(v==fa||v==son[u])continue;
        dfs(v,u,v);
    }
}

#define lc p<<1
#define rc p<<1|1
struct ahaha2{     //线段树太简单不解释
    int v;
}t[maxn<<2];
inline void pushup(int p){
    t[p].v=max(t[lc].v,t[rc].v);
}
void build(int p,int l,int r){
    if(l==r){t[p].v=a[b[l]];return;}
    int m=l+r>>1;
    build(lc,l,m);build(rc,m+1,r);
    pushup(p);
}
void update(int p,int l,int r,int L,int z){
    if(l==r){t[p].v=z;return;}
    int m=l+r>>1;
    if(m>=L)update(lc,l,m,L,z);
    else update(rc,m+1,r,L,z);
    pushup(p);
}
int query(int p,int l,int r,int L,int R){
    if(l>R||r<L)return 0;
    if(L<=l&&r<=R)return t[p].v;
    int m=l+r>>1;
    return max(query(lc,l,m,L,R),query(rc,m+1,r,L,R));
}

inline void solve_1(){    //从第0条边开始存,每条边存两次,所以输入的第i条边对应的是第2*i-2和第2*i-1条边,谁是儿子改谁
    int x=read()*2-2,w=read(),u=e[x].to,v=e[x+1].to;
    if(f[v]==u)update(1,1,n,in[v],w);
    else update(1,1,n,in[u],w);
}
inline void solve_2(){      //链上查询
    int x=read(),y=read(),ans=0;
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        ans=max(ans,query(1,1,n,in[top[x]],in[x]));    //由于存的是与父亲的距离,而top的父亲直接取所以此处无需加1
        x=f[top[x]];
    }
    if(dep[x]>dep[y])swap(x,y);
    ans=max(ans,query(1,1,n,in[x]+1,in[y]));     //但是最后一次存储必须加1否则会多询问一条边
    write(ans);putchar('\n');
}

int main(){memset(head,-1,sizeof head);
    n=read();
    for(int i=1;i<n;++i){
        int x=read(),y=read(),w=read();
        add(x,y,w);add(y,x,w);
    }
    tot=0;dfs(1,-1);dfs(1,-1,1);
    build(1,1,n);
    char z[10];
    while(1){
        scanf("%s",z);
        if(z[0]=='D')break;
        switch(z[0]){
            case 'C':solve_1();break;
            case 'Q':solve_2();break;
        }
    }
    return 0;
}