树链剖分入门——[kuangbin]树链剖分

树链剖分的本质就是将一棵树拆分成一段一段连续的区间,然后放在一起就可以用一棵单独的线段树处理区间问题,只需要将树上节点和线段树节点的对应关系求好就可以很方便的互相转换,而树上两点之间路径的相关问题就可以通过这拆分出来的一条一条的链来解决。

树链剖分的核心就是重轻链的剖分了,对于每一个节点,将其子节点中子树size最大的作为重链,递归处理,将树拆分,便可以由性质得出任意两个节点之间所需要经过树链的数量是logn级别,所以对于大量查询修改的问题,每一个查询修改的就可以在log^2n的时间内完成了,效率还是很高的。

树链剖分目前接触到的几道入门题都是两点内区间最大值或者求和查询,以及单点修改和区间修改,运用树链剖分转换成用线段树后就和一般的线段树问题差别不大了,唯一的区别就是如何在树链上走,使得两个节点见需要经过的树链都在线段树上进行操作。

A - Aragorn’s Story HDU - 3966

单点查询,区间修改。

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=51110;
int n,m,q;
struct edge{
    int to,nxt;
}e[maxn*2];
int tot,num;
int head[maxn],dep[maxn],fa[maxn],son[maxn],top[maxn],id[maxn],siz[maxn],eval[maxn];

void adde(int u,int v){
    e[tot].to=v;
    e[tot].nxt=head[u];
    head[u]=tot++;
}

void dfs1(int u,int f,int d){
    dep[u]=d,fa[u]=f,son[u]=0,siz[u]=1;
    for(int i=head[u];i!=-1;i=e[i].nxt){
        int v=e[i].to;
        if(v==f)continue;
        dfs1(v,u,d+1);
        siz[u]+=siz[v];
        if(siz[v]>siz[son[u]])son[u]=v;
    }
}

void dfs2(int u,int tp){
    top[u]=tp;id[u]=++num;
    if(son[u])dfs2(son[u],tp);
    for(int i=head[u];i!=-1;i=e[i].nxt){
        int v=e[i].to;
        if(v==fa[u]||v==son[u])continue;
        dfs2(v,v);
    }
}

int aa[maxn],tr[maxn*4],lazy[maxn*4];

void pushup(int cnt){
    tr[cnt]=tr[cnt<<1]+tr[cnt<<1|1];
}

void build(int cnt,int l,int r){
    lazy[cnt]=0;
    if(l==r){tr[cnt]=aa[l];return;}
    int mid=(l+r)>>1;
    build(cnt<<1,l,mid);
    build(cnt<<1|1,mid+1,r);
    pushup(cnt);
}


void pushdown(int cnt,int l,int r){
    if(lazy[cnt]){
        lazy[cnt<<1]+=lazy[cnt],lazy[cnt<<1|1]+=lazy[cnt];
        tr[cnt<<1]+=lazy[cnt]*l,tr[cnt<<1|1]+=lazy[cnt]*r;
    }
    lazy[cnt]=0;
}

void qupdate(int cnt,int l,int r,int lef,int rig,int val){
    if(lef<=l&&rig>=r){
        tr[cnt]+=val*(r-l+1);
        lazy[cnt]+=val;
        return;
    }
    int mid=(l+r)>>1;
    pushdown(cnt,mid-l+1,r-mid);
    if(lef<=mid)qupdate(cnt<<1,l,mid,lef,rig,val);
    if(rig>mid)qupdate(cnt<<1|1,mid+1,r,lef,rig,val);
    pushup(cnt);
}

int query(int cnt,int l,int r,int lef,int rig){
    if(lef<=l&&rig>=r)return tr[cnt];
    int mid=(l+r)>>1;
    int ans=0;
    pushdown(cnt,mid-l+1,r-mid);
    if(lef<=mid)ans+=query(cnt<<1,l,mid,lef,rig);
    if(rig>mid)ans+=query(cnt<<1|1,mid+1,r,lef,rig);
    return ans;
}

void tupdate(int u,int v,int val){
    int f1=top[u],f2=top[v];
    while(f1!=f2){
        if(dep[f1]<dep[f2]){
            swap(f1,f2),swap(u,v);
        }
        qupdate(1,1,n,id[f1],id[u],val);
        u=fa[f1],f1=top[u];
    }
    if(id[u]>id[v])swap(u,v);
    qupdate(1,1,n,id[u],id[v],val);
}


char op[10];
int main(){
    int cas,a,b,c;
    while(~scanf("%d%d%d",&n,&m,&q)){
        memset(head,-1,sizeof(head));
        num=tot=0;
        for(int i=1;i<=n;i++)scanf("%d",&eval[i]);
        for(int i=1;i<n;i++){
            scanf("%d%d",&a,&b);
            adde(a,b);
            adde(b,a);
        }
        dfs1(1,0,1);
        dfs2(1,1);
        for(int i=1;i<=n;i++)aa[id[i]]=eval[i];
        build(1,1,n);
        while(q--){
            scanf("%s",op);
                if(op[0]=='Q'){
                scanf("%d",&a);
                printf("%d\n",query(1,1,n,id[a],id[a]));
            }
            else if(op[0]=='I'){
                scanf("%d%d%d",&a,&b,&c);
                tupdate(a,b,c);
            }
            else{
                scanf("%d%d%d",&a,&b,&c);
                tupdate(a,b,-c);
            }
        }
    }
    return 0;
}

B - Housewife Wind POJ - 2763

存的信息是边权,树链上移动的处理略微有不同。区间查询单点修改。

//#include<bits/stdc++.h>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn=100011;
int n,q,s;
struct edge{
    int to,len,nxt;
}e[maxn*2];
int tot,num;
int head[maxn],dep[maxn],fa[maxn],son[maxn],top[maxn],id[maxn],siz[maxn],eval[maxn];
pair<int,int> ee[maxn];

void adde(int u,int v,int l){
    e[tot].to=v;
    e[tot].len=l;
    e[tot].nxt=head[u];
    head[u]=tot++;
}

void dfs1(int u,int f,int d){
    dep[u]=d,fa[u]=f,son[u]=0,siz[u]=1;
    for(int i=head[u];i!=-1;i=e[i].nxt){
        int v=e[i].to;
        if(v==f)continue;
        eval[v]=e[i].len;
        dfs1(v,u,d+1);
        siz[u]+=siz[v];
        if(siz[v]>siz[son[u]])son[u]=v;
    }
}

void dfs2(int u,int tp){
    top[u]=tp;id[u]=++num;
    if(son[u])dfs2(son[u],tp);
    for(int i=head[u];i!=-1;i=e[i].nxt){
        int v=e[i].to;
        if(v==fa[u]||v==son[u])continue;
        dfs2(v,v);
    }
}

int aa[maxn],tr[maxn*4];

void pushup(int cnt){
    tr[cnt]=tr[cnt<<1]+tr[cnt<<1|1];
}

void build(int cnt,int l,int r){
    if(l==r){tr[cnt]=aa[l];return;}
    int mid=(l+r)>>1;
    build(cnt<<1,l,mid);
    build(cnt<<1|1,mid+1,r);
    pushup(cnt);
}

void change(int cnt,int l,int r,int p,int val){
    if(l==r){tr[cnt]=val;return;}
    int mid=(l+r)>>1;
    if(p<=mid)change(cnt<<1,l,mid,p,val);
    else change(cnt<<1|1,mid+1,r,p,val);
    pushup(cnt);
}

int query(int cnt,int l,int r,int lef,int rig){
    if(lef<=l&&rig>=r)return tr[cnt];
    int mid=(l+r)>>1;
    int ans=0;
    if(lef<=mid)ans+=query(cnt<<1,l,mid,lef,rig);
    if(rig>mid)ans+=query(cnt<<1|1,mid+1,r,lef,rig);
    return ans;
}

int tquery(int u,int v){
    int f1=top[u],f2=top[v];
    int ans=0;
    while(f1!=f2){
        if(dep[f1]<dep[f2]){
            swap(f1,f2);swap(u,v);
        }
        ans+=query(1,1,n,id[f1],id[u]);
        u=fa[f1],f1=top[u];
    }
    if(u==v)return ans;
    if(dep[u]>dep[v])swap(u,v);
    ans+=query(1,1,n,id[son[u]],id[v]);
    return ans;
}

int main(){
    int cas,a,b,c;
    while(~scanf("%d%d%d",&n,&q,&s)){
        memset(head,-1,sizeof(head));
        num=tot=0;
        for(int i=1;i<n;i++){
            scanf("%d%d%d",&a,&b,&c);
            ee[i].first=a,ee[i].second=b;
            adde(a,b,c);
            adde(b,a,c);
        }
        dfs1(1,0,1);
        dfs2(1,1);
        for(int i=1;i<=n;i++)aa[id[i]]=eval[i];
        build(1,1,n);
        while(q--){
            scanf("%d",&c);
            if(c==0){
                scanf("%d",&a);
                printf("%d\n",tquery(a,s));
                s=a;
            }
            else{
                scanf("%d%d",&a,&b);
                int cnte=ee[a].first;
                if(dep[ee[a].second]>dep[cnte])cnte=ee[a].second;
                change(1,1,n,id[cnte],b);
            }
        }
    }
    return 0;
}

C - Tree POJ - 3237

除了单点查询和区间修改操作之外还有一个区间取反的操作,线段树就需要存一个最大值和最小值,去反的时候就将最大值和最小值取负数后交换即可。

//#include<bits/stdc++.h>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn=100015;
const int inf=0x3f3f3f3f;
int n;
struct edge{
    int to,len,nxt;
}e[maxn*2];
int tot,num;
int head[maxn],dep[maxn],fa[maxn],son[maxn],top[maxn],id[maxn],siz[maxn],eval[maxn];
pair<int,int> ee[maxn];

void adde(int u,int v,int l){
    e[tot].to=v;
    e[tot].len=l;
    e[tot].nxt=head[u];
    head[u]=tot++;
}

void dfs1(int u,int f,int d){
    dep[u]=d,fa[u]=f,son[u]=0,siz[u]=1;
    for(int i=head[u];i!=-1;i=e[i].nxt){
        int v=e[i].to;
        if(v==f)continue;
        eval[v]=e[i].len;
        dfs1(v,u,d+1);
        siz[u]+=siz[v];
        if(siz[v]>siz[son[u]])son[u]=v;
    }
}

void dfs2(int u,int tp){
    top[u]=tp;id[u]=num++;
    if(son[u])dfs2(son[u],tp);
    for(int i=head[u];i!=-1;i=e[i].nxt){
        int v=e[i].to;
        if(v==fa[u]||v==son[u])continue;
        dfs2(v,v);
    }
}

int aa[maxn],maxx[maxn*4],minn[maxn*4];
bool add[maxn*4];

void pushup(int cnt){
    maxx[cnt]=max(maxx[cnt<<1],maxx[cnt<<1|1]);
    minn[cnt]=min(minn[cnt<<1],minn[cnt<<1|1]);
}

void build(int cnt,int l,int r){
    add[cnt]=0;
    if(l==r){maxx[cnt]=minn[cnt]=aa[l];return;}
    int mid=(l+r)>>1;
    build(cnt<<1,l,mid);
    build(cnt<<1|1,mid+1,r);
    pushup(cnt);
}

void nega(int cnt){
    swap(maxx[cnt],minn[cnt]);
    maxx[cnt]=-maxx[cnt];
    minn[cnt]=-minn[cnt];
}

void push_down(int cnt,int lef,int rig){
    if(add[cnt]){
        add[cnt<<1]=!add[cnt<<1],add[cnt<<1|1]=!add[cnt<<1|1];
        nega(cnt<<1);
        nega(cnt<<1|1);
        add[cnt]=0;
    }
}

void change(int cnt,int l,int r,int p,int val){
    if(l==r){
        maxx[cnt]=val;
        minn[cnt]=val;
        return;
    }
    int mid=(l+r)>>1;
    push_down(cnt,1,1);
    if(p<=mid)change(cnt<<1,l,mid,p,val);
    else change(cnt<<1|1,mid+1,r,p,val);
    pushup(cnt);
}

void qupdate(int cnt,int l,int r,int lef,int rig,int val){
    if(lef<=l&&rig>=r){
        nega(cnt);
        add[cnt]=!add[cnt];
        return;
    }
    int mid=(l+r)>>1;
    push_down(cnt,mid-l+1,r-mid);
    if(lef<=mid)qupdate(cnt<<1,l,mid,lef,rig,val);
    if(rig>mid)qupdate(cnt<<1|1,mid+1,r,lef,rig,val);
    pushup(cnt);
}

int query(int cnt,int l,int r,int lef,int rig){
    if(lef<=l&&rig>=r)return maxx[cnt];
    int mid=(l+r)>>1;
    int ans=-inf;
    push_down(cnt,mid-l+1,r-mid);
    if(lef<=mid)ans=max(ans,query(cnt<<1,l,mid,lef,rig));
    if(rig>mid)ans=max(ans,query(cnt<<1|1,mid+1,r,lef,rig));
    return ans;
}

int tquery(int u,int v){
    int f1=top[u],f2=top[v];
    int ans=-inf;
    while(f1!=f2){
        if(dep[f1]<dep[f2]){
            swap(f1,f2);swap(u,v);
        }
        ans=max(ans,query(1,1,n,id[f1],id[u]));
        u=fa[f1],f1=top[u];
    }
    if(u==v)return ans;
    if(dep[u]>dep[v])swap(u,v);
    ans=max(ans,query(1,1,n,id[son[u]],id[v]));
    return ans;
}

void tupdate(int u,int v,int val){
    int f1=top[u],f2=top[v];
    while(f1!=f2){
        if(dep[f1]<dep[f2]){
            swap(f1,f2);swap(u,v);
        }
        qupdate(1,1,n,id[f1],id[u],val);
        u=fa[f1];f1=top[u];
    }
    if(u==v)return;
    if(dep[u]>dep[v])swap(u,v);
    qupdate(1,1,n,id[son[u]],id[v],val);
}

char op[10];
int main(){
    int cas,a,b,c;
    scanf("%d",&cas);
    while(cas--){
        scanf("%d",&n);
        memset(head,-1,sizeof(head));
        num=tot=0;
        for(int i=1;i<n;i++){
            scanf("%d%d%d",&a,&b,&c);
            ee[i].first=a,ee[i].second=b;
            adde(a,b,c);
            adde(b,a,c);
        }
        dfs1(1,0,1);
        dfs2(1,1);
        for(int i=1;i<=n;i++)aa[id[i]]=eval[i];
        n--;
        build(1,1,n);
        while(1){
            scanf("%s",op);
            if(op[0]=='D')break;
            if(op[0]=='Q'){
                scanf("%d%d",&a,&b);
                printf("%d\n",tquery(a,b));
            }
            else if(op[0]=='C'){
                scanf("%d%d",&a,&b);
                int cnte=ee[a].first;
                if(dep[ee[a].second]>dep[cnte])cnte=ee[a].second;
                change(1,1,n,id[cnte],b);
            }
            else{
                scanf("%d%d",&a,&b);
                tupdate(a,b,1);
            }
        }
    }
    return 0;
}

D - 染色 HYSBZ - 2243(未AC)

求区间不连续段的数量,由于线段树和树链都将原本连续的路径拆分开处理了,所以单单把每一段查询加起来就会导致多余的计算,所以将两段区间的统计数量相加时应该判断左段的右边和右段的左边是否相等,相等的话答案-1.
看别人的做法都是线段树多存两个数据(左端点和右端点的颜色),我的做法则是多写了一个查询颜色的函数来查询两个临界点的颜色,然而就是wa= =。

//#include<bits/stdc++.h>
#pragma comment(linker, "/STACK:1024000000,1024000000");
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn=301115;
const int inf=0x3f3f3f3f;
int n,m;
struct edge{
    int to,nxt;
}e[maxn*2];
int tot,nu;
int head[maxn],dep[maxn],fa[maxn],son[maxn],top[maxn],id[maxn],siz[maxn],eval[maxn];
pair<int,int> ee[maxn];

void adde(int u,int v){
    e[tot].to=v;
    e[tot].nxt=head[u];
    head[u]=tot++;
}

void dfs1(int u,int f,int d){
    dep[u]=d,fa[u]=f,son[u]=0,siz[u]=1;
    for(int i=head[u];i!=-1;i=e[i].nxt){
        int v=e[i].to;
        if(v==f)continue;
        dfs1(v,u,d+1);
        siz[u]+=siz[v];
        if(siz[v]>siz[son[u]])son[u]=v;
    }
}

void dfs2(int u,int tp){
    top[u]=tp;id[u]=++nu;
    if(son[u])dfs2(son[u],tp);
    for(int i=head[u];i!=-1;i=e[i].nxt){
        int v=e[i].to;
        if(v==fa[u]||v==son[u])continue;
        dfs2(v,v);
    }
}

int aa[maxn],num[maxn*4],cor[maxn*4],lazy[maxn*4];

void push_down(int cnt){
    if(lazy[cnt]!=-1){
        lazy[cnt<<1]=lazy[cnt<<1|1]=lazy[cnt];
        cor[cnt<<1]=cor[cnt<<1|1]=cor[cnt];
        num[cnt<<1]=num[cnt<<1|1]=1;
        lazy[cnt]=-1;
    }
}

int querycor(int cnt,int l,int r,int p){
    if(l==r){
        return cor[cnt];
    }
    int mid=(l+r)>>1;
    push_down(cnt);
    if(p<=mid)return querycor(cnt<<1,l,mid,p);
    else return querycor(cnt<<1|1,mid+1,r,p);
}

void pushup(int cnt,int a,int b){
    num[cnt]=num[cnt<<1]+num[cnt<<1|1];
    if(querycor(1,1,n,a)==querycor(1,1,n,b))num[cnt]--;
    if(num[cnt]==1)cor[cnt]=cor[cnt<<1];
}

void build(int cnt,int l,int r){
    lazy[cnt]=-1;
    cor[cnt]=-1;
    if(l==r){cor[cnt]=aa[l];num[cnt]=1;return;}
    int mid=(l+r)>>1;
    build(cnt<<1,l,mid);
    build(cnt<<1|1,mid+1,r);
    num[cnt]=num[cnt<<1]+num[cnt<<1|1];
    if(aa[mid]==aa[mid+1])num[cnt]--;
    if(num[cnt]==1)cor[cnt]=cor[cnt<<1];
}

void qupdate(int cnt,int l,int r,int lef,int rig,int val){
    if(lef<=l&&r<=rig){
        lazy[cnt]=cor[cnt]=val;
        num[cnt]=1;
        return;
    }
    int mid=(l+r)>>1;
    push_down(cnt);
    if(lef<=mid)qupdate(cnt<<1,l,mid,lef,rig,val);
    if(rig>mid)qupdate(cnt<<1|1,mid+1,r,lef,rig,val);
    pushup(cnt,mid,mid+1);
}


int querynum(int cnt,int l,int r,int lef,int rig){
    if(lef<=l&&rig>=r)return num[cnt];
    int mid=(l+r)>>1;
    int ans=0;
    push_down(cnt);
    int cc=0;
    if(lef<=mid)cc++,ans+=querynum(cnt<<1,l,mid,lef,rig);
    if(rig>mid)cc++,ans+=querynum(cnt<<1|1,mid+1,r,lef,rig);
    if(cc==2)if(querycor(1,1,n,mid)==querycor(1,1,n,mid+1))ans--;
    return ans;
}

int tnum(int u,int v){
    int f1=top[u],f2=top[v];
    int ans=0;
    while(f1!=f2){
        if(dep[f1]<dep[f2]){
            swap(f1,f2);swap(u,v);
        }
        ans+=querynum(1,1,n,id[f1],id[u]);
        int aaa=querycor(1,1,n,id[f1]);
        int bbb=querycor(1,1,n,id[fa[f1]]);
        if(aaa==bbb)ans--;
        u=fa[f1],f1=top[u];
    }
    if(dep[u]>dep[v])swap(u,v);
    ans+=querynum(1,1,n,id[u],id[v]);
    return ans;
}

void tupdate(int u,int v,int val){
    int f1=top[u],f2=top[v];
    while(f1!=f2){
        if(dep[f1]<dep[f2]){
            swap(f1,f2);swap(u,v);
        }
        qupdate(1,1,n,id[f1],id[u],val);
        u=fa[f1];f1=top[u];
    }
    if(dep[u]>dep[v])swap(u,v);
    qupdate(1,1,n,id[u],id[v],val);
}

char op[10];
int main(){
    int a,b,c;
    while(~scanf("%d%d",&n,&m)){
        memset(head,-1,sizeof(head));
        nu=tot=0;
        for(int i=1;i<=n;i++)scanf("%d",&eval[i]);
        for(int i=1;i<n;i++){
            scanf("%d%d",&a,&b);
            adde(a,b);
            adde(b,a);
        }
        dfs1(1,0,1);
        dfs2(1,1);
        for(int i=1;i<=n;i++)aa[id[i]]=eval[i];
        build(1,1,n);
        while(m--){
            scanf("%s",op);
            if(op[0]=='C'){
                scanf("%d%d%d",&a,&b,&c);
                tupdate(a,b,c);
            }
            else{
                scanf("%d%d",&a,&b);
                printf("%d\n",tnum(a,b));
            }
        }
    }
    return 0;
}

E - 树的统计Count HYSBZ - 1036

区间查询单点修改,只是要统计两个数据,没什么其他变化。

//#include<bits/stdc++.h>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn=100015;
const int inf=0x3f3f3f3f;
int n,q;
struct edge{
    int to,nxt;
}e[maxn*2];
int tot,num;
int head[maxn],dep[maxn],fa[maxn],son[maxn],top[maxn],id[maxn],siz[maxn],eval[maxn];
pair<int,int> ee[maxn];

void adde(int u,int v){
    e[tot].to=v;
    e[tot].nxt=head[u];
    head[u]=tot++;
}

void dfs1(int u,int f,int d){
    dep[u]=d,fa[u]=f,son[u]=0,siz[u]=1;
    for(int i=head[u];i!=-1;i=e[i].nxt){
        int v=e[i].to;
        if(v==f)continue;
        dfs1(v,u,d+1);
        siz[u]+=siz[v];
        if(siz[v]>siz[son[u]])son[u]=v;
    }
}

void dfs2(int u,int tp){
    top[u]=tp;id[u]=++num;
    if(son[u])dfs2(son[u],tp);
    for(int i=head[u];i!=-1;i=e[i].nxt){
        int v=e[i].to;
        if(v==fa[u]||v==son[u])continue;
        dfs2(v,v);
    }
}

int aa[maxn],maxx[maxn*4],sum[maxn*4];

void pushup(int cnt){
    maxx[cnt]=max(maxx[cnt<<1],maxx[cnt<<1|1]);
    sum[cnt]=sum[cnt<<1]+sum[cnt<<1|1];
}

void build(int cnt,int l,int r){
    if(l==r){maxx[cnt]=sum[cnt]=aa[l];return;}
    int mid=(l+r)>>1;
    build(cnt<<1,l,mid);
    build(cnt<<1|1,mid+1,r);
    pushup(cnt);
}

void change(int cnt,int l,int r,int p,int val){
    if(l==r){
        maxx[cnt]=val;
        sum[cnt]=val;
        return;
    }
    int mid=(l+r)>>1;
    if(p<=mid)change(cnt<<1,l,mid,p,val);
    else change(cnt<<1|1,mid+1,r,p,val);
    pushup(cnt);
}

int querymax(int cnt,int l,int r,int lef,int rig){
    if(lef<=l&&rig>=r)return maxx[cnt];
    int mid=(l+r)>>1;
    int ans=-inf;
    if(lef<=mid)ans=max(ans,querymax(cnt<<1,l,mid,lef,rig));
    if(rig>mid)ans=max(ans,querymax(cnt<<1|1,mid+1,r,lef,rig));
    return ans;
}

int querysum(int cnt,int l,int r,int lef,int rig){
    if(lef<=l&&rig>=r)return sum[cnt];
    int mid=(l+r)>>1;
    int ans=0;
    if(lef<=mid)ans+=querysum(cnt<<1,l,mid,lef,rig);
    if(rig>mid)ans+=querysum(cnt<<1|1,mid+1,r,lef,rig);
    return ans;
}

int tmax(int u,int v){
    int f1=top[u],f2=top[v];
    int ans=-inf;
    while(f1!=f2){
        if(dep[f1]<dep[f2]){
            swap(f1,f2);swap(u,v);
        }
        ans=max(ans,querymax(1,1,n,id[f1],id[u]));
        u=fa[f1],f1=top[u];
    }
    if(dep[u]>dep[v])swap(u,v);
    ans=max(ans,querymax(1,1,n,id[u],id[v]));
    return ans;
}

int tsum(int u,int v){
    int f1=top[u],f2=top[v];
    int ans=0;
    while(f1!=f2){
        if(dep[f1]<dep[f2]){
            swap(f1,f2);swap(u,v);
        }
        ans+=querysum(1,1,n,id[f1],id[u]);
        u=fa[f1],f1=top[u];
    }
    if(dep[u]>dep[v])swap(u,v);
    ans+=querysum(1,1,n,id[u],id[v]);
    return ans;
}

char op[10];
int main(){
    int a,b,c;
    while(~scanf("%d",&n)){
        memset(head,-1,sizeof(head));
        num=tot=0;
        for(int i=1;i<n;i++){
            scanf("%d%d",&a,&b);
            adde(a,b);
            adde(b,a);
        }
        dfs1(1,0,1);
        dfs2(1,1);
        for(int i=1;i<=n;i++)scanf("%d",&aa[id[i]]);
        build(1,1,n);
        scanf("%d",&q);
        while(q--){
            scanf("%s",op);
            scanf("%d%d",&b,&c);
            if(op[0]=='C'){
                change(1,1,n,id[b],c);
            }
            else if(op[1]=='M'){
                printf("%d\n",tmax(b,c));
            }
            else {
                printf("%d\n",tsum(b,c));
            }
        }
    }
    return 0;
}

G - 过路费 FZU - 2082

拿前面的代码改改就可以了- -

//#include<bits/stdc++.h>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn=50015;
int n,m;
struct edge{
    int to,len,nxt;
}e[maxn*2];
int tot,num;
int head[maxn],dep[maxn],fa[maxn],son[maxn],top[maxn],id[maxn],siz[maxn],eval[maxn];
pair<int,int> ee[maxn];

void adde(int u,int v,int l){
    e[tot].to=v;
    e[tot].len=l;
    e[tot].nxt=head[u];
    head[u]=tot++;
}

void dfs1(int u,int f,int d){
    dep[u]=d,fa[u]=f,son[u]=0,siz[u]=1;
    for(int i=head[u];i!=-1;i=e[i].nxt){
        int v=e[i].to;
        if(v==f)continue;
        eval[v]=e[i].len;
        dfs1(v,u,d+1);
        siz[u]+=siz[v];
        if(siz[v]>siz[son[u]])son[u]=v;
    }
}

void dfs2(int u,int tp){
    top[u]=tp;id[u]=++num;
    if(son[u])dfs2(son[u],tp);
    for(int i=head[u];i!=-1;i=e[i].nxt){
        int v=e[i].to;
        if(v==fa[u]||v==son[u])continue;
        dfs2(v,v);
    }
}

ll aa[maxn],tr[maxn*4];

void pushup(int cnt){
    tr[cnt]=tr[cnt<<1]+tr[cnt<<1|1];
}

void build(int cnt,int l,int r){
    if(l==r){tr[cnt]=aa[l];return;}
    int mid=(l+r)>>1;
    build(cnt<<1,l,mid);
    build(cnt<<1|1,mid+1,r);
    pushup(cnt);
}

void change(int cnt,int l,int r,int p,int val){
    if(l==r){tr[cnt]=val;return;}
    int mid=(l+r)>>1;
    if(p<=mid)change(cnt<<1,l,mid,p,val);
    else change(cnt<<1|1,mid+1,r,p,val);
    pushup(cnt);
}

int query(int cnt,int l,int r,int lef,int rig){
    if(lef<=l&&rig>=r)return tr[cnt];
    int mid=(l+r)>>1;
    ll ans=0;
    if(lef<=mid)ans+=query(cnt<<1,l,mid,lef,rig);
    if(rig>mid)ans+=query(cnt<<1|1,mid+1,r,lef,rig);
    return ans;
}

ll tquery(int u,int v){
    int f1=top[u],f2=top[v];
    ll ans=0;
    while(f1!=f2){
        if(dep[f1]<dep[f2]){
            swap(f1,f2);swap(u,v);
        }
        ans+=query(1,1,n,id[f1],id[u]);
        u=fa[f1],f1=top[u];
    }
    if(u==v)return ans;
    if(dep[u]>dep[v])swap(u,v);
    ans+=query(1,1,n,id[son[u]],id[v]);
    return ans;
}

int main(){
    int cas,a,b,c;
    while(~scanf("%d%d",&n,&m)){
        memset(head,-1,sizeof(head));
        num=tot=0;
        for(int i=1;i<n;i++){
            scanf("%d%d%d",&a,&b,&c);
            ee[i].first=a,ee[i].second=b;
            adde(a,b,c);
            adde(b,a,c);
        }
        dfs1(1,0,1);
        dfs2(1,1);
        for(int i=1;i<=n;i++)aa[id[i]]=eval[i];
        build(1,1,n);
        while(m--){
            scanf("%d",&c);
            scanf("%d%d",&a,&b);
            if(c==1){
                printf("%d\n",tquery(a,b));
            }
            else{
                int cnte=ee[a].first;
                if(dep[ee[a].second]>dep[cnte])cnte=ee[a].second;
                change(1,1,n,id[cnte],b);
            }
        }
    }
    return 0;
}

H - Aladdin and the Return Journey LightOJ - 1348

也是那前面的代码改改就行了。

I - Query on a tree SPOJ - QTREE

单点修改区间查询。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=10015;
int n;
struct edge{
    int to,len,nxt;
}e[maxn*2];
int tot,num;
int head[maxn],dep[maxn],fa[maxn],son[maxn],top[maxn],id[maxn],siz[maxn],eval[maxn];
pair<int,int> ee[maxn];

void adde(int u,int v,int l){
    e[tot].to=v;
    e[tot].len=l;
    e[tot].nxt=head[u];
    head[u]=tot++;
}

void dfs1(int u,int f,int d){
    dep[u]=d,fa[u]=f,son[u]=0,siz[u]=1;
    for(int i=head[u];i!=-1;i=e[i].nxt){
        int v=e[i].to;
        if(v==f)continue;
        eval[v]=e[i].len;
        dfs1(v,u,d+1);
        siz[u]+=siz[v];
        if(siz[v]>siz[son[u]])son[u]=v;
    }
}

void dfs2(int u,int tp){
    top[u]=tp;id[u]=++num;
    if(son[u])dfs2(son[u],tp);
    for(int i=head[u];i!=-1;i=e[i].nxt){
        int v=e[i].to;
        if(v==fa[u]||v==son[u])continue;
        dfs2(v,v);
    }
}

int aa[maxn],tr[maxn*4];

void pushup(int cnt){
    tr[cnt]=max(tr[cnt<<1],tr[cnt<<1|1]);
}

void build(int cnt,int l,int r){
    if(l==r){tr[cnt]=aa[l];return;}
    int mid=(l+r)>>1;
    build(cnt<<1,l,mid);
    build(cnt<<1|1,mid+1,r);
    pushup(cnt);
}

void change(int cnt,int l,int r,int p,int val){
    if(l==r){tr[cnt]=val;return;}
    int mid=(l+r)>>1;
    if(p<=mid)change(cnt<<1,l,mid,p,val);
    else change(cnt<<1|1,mid+1,r,p,val);
    pushup(cnt);
}

int query(int cnt,int l,int r,int lef,int rig){
    if(lef<=l&&rig>=r)return tr[cnt];
    int mid=(l+r)>>1;
    int ans=0;
    if(lef<=mid)ans=max(ans,query(cnt<<1,l,mid,lef,rig));
    if(rig>mid)ans=max(ans,query(cnt<<1|1,mid+1,r,lef,rig));
    return ans;
}

int tquery(int u,int v){
    int f1=top[u],f2=top[v];
    int ans=0;
    while(f1!=f2){
        if(dep[f1]<dep[f2]){
            swap(f1,f2);swap(u,v);
        }
        ans=max(ans,query(1,1,n,id[f1],id[u]));
        u=fa[f1],f1=top[u];
    }
    if(u==v)return ans;
    if(dep[u]>dep[v])swap(u,v);
    ans=max(ans,query(1,1,n,id[son[u]],id[v]));
    return ans;
}

char op[10];
int main(){
    int cas,a,b,c;
    scanf("%d",&cas);
    while(cas--){
        scanf("%d",&n);
        memset(head,-1,sizeof(head));
        num=tot=0;
        for(int i=1;i<n;i++){
            scanf("%d%d%d",&a,&b,&c);
            ee[i].first=a,ee[i].second=b;
            adde(a,b,c);
            adde(b,a,c);
        }
        dfs1(1,0,1);
        dfs2(1,1);
        for(int i=1;i<=n;i++)aa[id[i]]=eval[i];
        build(1,1,n);
        while(1){
            scanf("%s",op);
            if(op[0]=='D')break;
            if(op[0]=='Q'){
                scanf("%d%d",&a,&b);
                printf("%d\n",tquery(a,b));
            }
            else{
                scanf("%d%d",&a,&b);
                int cnte=ee[a].first;
                if(dep[ee[a].second]>dep[cnte])cnte=ee[a].second;
                change(1,1,n,id[cnte],b);
            }
        }
    }
    return 0;
}

(这个专题的题目的相似程度太高了= =。。。)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值