P1505 [国家集训队]旅游(树链剖分)

该博客详细介绍了P1505旅游问题,利用树链剖分的方法解决。内容涉及如何为边编号,以及在dfs序基础上进行边权修改的策略。强调了在同一条重链中,只修改深度较深节点的边权。
摘要由CSDN通过智能技术生成

题目链接:P1505 [国家集训队]旅游
对于边的剖分,将每条边的编号赋值为其后向点的dfs序编号,因为在dfs过程中,儿子节点有且只有一个直接父亲,那么可以将这条边的编号写作这个儿子的编号。

修改一条链上的边权,与点权不同的是,当两个点处于同一条重链的时候,深度较浅的那个点对应的边不属于我们修改的范围:

void updatachain(int x,int y){
    while(topp[x]!=topp[y]){
        if(dep[topp[x]]<dep[topp[y]]) swap(x,y);
        updata(1,n,1,dfn[topp[x]],dfn[x]);
        //cout<<dfn[topp[x]]<<" "<<dfn[x]<<endl;
        x=fa[topp[x]];
    }
    if(dep[x]>dep[y]) swap(x,y);
    //cout<<dfn[x]+1<<" "<<dfn[y]<<endl;
    updata(1,n,1,dfn[x]+1,dfn[y]);
}

预处理比点剖多了一个儿子节点的直连边的边权,其余都一致。


#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+7;

int maxx[maxn<<2|1],minn[maxn<<2|1],sum[maxn<<2|1],lazy[maxn<<2|1];

struct Edge{
    int v,w,next;
}edge[maxn<<1];

int top,head[maxn];
void init(){
    top=0;
    memset(head,-1,sizeof(head));
}

void add(int u,int v,int w){
    edge[top].v=v;
    edge[top].w=w;
    edge[top].next=head[u];
    head[u]=top++;
}
//深度,dfs编号对应的边权,dfs序,编号,重链最顶端,子树大小,重儿子,直接父亲,儿子与父亲的直连边权;
int dep[maxn],val[maxn],dfn[maxn],num,topp[maxn],size1[maxn],son[maxn],fa[maxn],faedge[maxn];
bool vis[maxn];
void dfs1(int u){
    size1[u]=1;
    vis[u]=1;
    int v;
    int maxx=-1;
    for(int i=head[u];i!=-1;i=edge[i].next){
        v=edge[i].v;
        if(vis[v]) continue;
        dep[v]=dep[u]+1;
        fa[v]=u;
        faedge[v]=edge[i].w;
        dfs1(v);
        size1[u]+=size1[v];
        if(size1[v]>maxx){
            son[u]=v;
            maxx=size1[v];
        }
    }
}

void dfs2(int u,int t,int lastw){
    topp[u]=t;
    dfn[u]=++num;
    val[num]=lastw;
    if(!son[u]) return ;
    dfs2(son[u],t,faedge[son[u]]);
    int v,w;
    for(int i=head[u];i!=-1;i=edge[i].next){
        v=edge[i].v;
        w=edge[i].w;
        if(dfn[v]||v==son[u]) continue;
        dfs2(v,v,w);
    }
}

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

void pushdown(int k){
    int x;
    if(lazy[k]){
        lazy[k<<1]^=1;
        lazy[k<<1|1]^=1;
        sum[k<<1]*=-1;
        sum[k<<1|1]*=-1;
        swap(maxx[k<<1],minn[k<<1]);
        maxx[k<<1]*=-1,minn[k<<1]*=-1;
        swap(maxx[k<<1|1],minn[k<<1|1]);
        maxx[k<<1|1]*=-1,minn[k<<1|1]*=-1;
        lazy[k]=0;
    }
}

void build(int l,int r,int k){
    lazy[k]=0;
    if(l==r){
        sum[k]=val[l];
        maxx[k]=val[l];
        minn[k]=val[l];
        return ;
    }
    int mid=(l+r)>>1;
    build(l,mid,k<<1);
    build(mid+1,r,k<<1|1);
    pushup(k);
}

void updata_id(int l,int r,int k,int id,int val){
    if(l==r){
        sum[k]=minn[k]=maxx[k]=val;
        return ;
    }
    pushdown(k);
    int mid=(l+r)>>1;
    if(id<=mid) updata_id(l,mid,k<<1,id,val);
    else updata_id(mid+1,r,k<<1|1,id,val);
    pushup(k);
}
int n;
void updata(int l,int r,int k,int L,int R){
    if(L>R) return ;
    if(l>R||r<L) return ;
    if(l>=L&&r<=R){
        lazy[k]^=1;
        sum[k]*=-1;
        swap(maxx[k],minn[k]);
        maxx[k]*=-1,minn[k]*=-1;
        return ;
    }
    int mid=(l+r)>>1;
    pushdown(k);
    if(L<=mid) updata(l,mid,k<<1,L,R);
    if(R>mid) updata(mid+1,r,k<<1|1,L,R);
    pushup(k);
}

int myfindsum(int l,int r,int k,int L,int R){
    //if(L>R) return 0;
    if(l>R||r<L) return 0;
    if(l>=L&&r<=R) return sum[k];
    int mid=(l+r)>>1;
    pushdown(k);
    int res=0;
    if(L<=mid) res+=myfindsum(l,mid,k<<1,L,R);
    if(R>mid) res+=myfindsum(mid+1,r,k<<1|1,L,R);
    pushup(k);
    return res;
}
const int inf=0x3f3f3f3f;
int myfindmax(int l,int r,int k,int L,int R){
    //if(L>R) return -inf;
    if(l>R||r<L) return -inf;
    if(l>=L&&r<=R) return maxx[k];
    int mid=(l+r)>>1;
    pushdown(k);
    int res=-inf;
    if(L<=mid) res=max(res,myfindmax(l,mid,k<<1,L,R));
    if(R>mid) res=max(res,myfindmax(mid+1,r,k<<1|1,L,R));
    pushup(k);
    return res;
}

int myfindmin(int l,int r,int k,int L,int R){
    //if(L>R) return inf;
    if(l>R||r<L) return inf;
    if(l>=L&&r<=R) return minn[k];
    int mid=(l+r)>>1;
    pushdown(k);
    int res=inf;
    if(L<=mid) res=min(res,myfindmin(l,mid,k<<1,L,R));
    if(R>mid) res=min(res,myfindmin(mid+1,r,k<<1|1,L,R));
    pushup(k);
    return res;
}
//1 sum,2 max,3 min;
int myfindchain(int x,int y,int id){
    int res=0;
    if(id==2) res=-inf;
    else if(id==3) res=inf;
    while(topp[x]!=topp[y]){
        if(dep[topp[x]]<dep[topp[y]]) swap(x,y);
        if(id==1) res+=myfindsum(1,n,1,dfn[topp[x]],dfn[x]);
        else if(id==2) res=max(res,myfindmax(1,n,1,dfn[topp[x]],dfn[x]));
        else res=min(res,myfindmin(1,n,1,dfn[topp[x]],dfn[x]));
        //cout<<dfn[topp[x]]<<" "<<dfn[x]<<endl;
        x=fa[topp[x]];
    }
    if(dep[x]>dep[y]) swap(x,y);
    if(id==1) res+=myfindsum(1,n,1,dfn[x]+1,dfn[y]);
    else if(id==2) res=max(res,myfindmax(1,n,1,dfn[x]+1,dfn[y]));
    else res=min(res,myfindmin(1,n,1,dfn[x]+1,dfn[y]));
    //cout<<dfn[x]+1<<" "<<dfn[y]<<endl;
    return res;
}

void updatachain(int x,int y){
    while(topp[x]!=topp[y]){
        if(dep[topp[x]]<dep[topp[y]]) swap(x,y);
        updata(1,n,1,dfn[topp[x]],dfn[x]);
        //cout<<dfn[topp[x]]<<" "<<dfn[x]<<endl;
        x=fa[topp[x]];
    }
    if(dep[x]>dep[y]) swap(x,y);
    //cout<<dfn[x]+1<<" "<<dfn[y]<<endl;
    updata(1,n,1,dfn[x]+1,dfn[y]);
}
char s[9];

int main(){
    init();
    //freopen("in.txt","r",stdin);
    //freopen("out1.txt","w",stdout);
    scanf("%d",&n);
    int u,v,w;
    for(int i=1;i<n;++i){
        scanf("%d%d%d",&u,&v,&w);
        ++u,++v;
        add(u,v,w);
        add(v,u,w);
    }
    dep[1]=1;
    dfs1(1);
    dfs2(1,1,0);
    build(1,n,1);
    int q;
    scanf("%d",&q);
    while(q--){
        scanf("%s%d%d",s,&u,&v);
        if(s[0]=='C'){
            --u;
            int point=(dep[edge[u<<1].v]>dep[edge[u<<1|1].v]?edge[u<<1].v:edge[u<<1|1].v);
            updata_id(1,n,1,dfn[point],v);
        }
        else if(s[0]=='N') updatachain(u+1,v+1);
        else if(s[0]=='S') printf("%d\n",myfindchain(u+1,v+1,1));
        else if(s[1]=='A') printf("%d\n",myfindchain(u+1,v+1,2));
        else printf("%d\n",myfindchain(u+1,v+1,3));
    }

    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值