zoukankan      html  css  js  c++  java
  • POJ 3237 Tree (树链剖分+边权转点权)

    <题目链接>

    题目大意:

    给定一棵树,该树带有边权,现在对该树进行三种操作:

    一:改变指定编号边的边权;

    二:对树上指定路径的边权全部取反;

    三:查询树上指定路径的最大边权值。

    解题分析:

    本题虽然只需要查询某段区间的最大值,但是线段树的每个节点都应该有最大和最小值,因为对区间取反之后,这段区间的最大值的相反数为最小值,最小值的相反数为最大值。然后就是注意对 lazy标记的操作。

    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #define Lson l,mid,rt<<1
    #define Rson mid+1,r,rt<<1|1
    using namespace std;
    const int M = 1e5+7;
    typedef long long ll;
    #define INF 1e15
    int T,n,pp;
    int cnt,tot,head[M],p[M];
    int sz[M],son[M],dep[M],f[M],top[M],rnk[M],id[M];
    ll a[M];
    char s[10];
    struct edge
    {
        int v,next;
        ll w;
    }e[M<<1];
    struct node
    {
        ll mx,mn;int lazy;    
    }tree[M<<2];
    void init(){
        tot=cnt=0;memset(head,-1,sizeof(head));
    }
    void add(int u,int v,ll w){
        e[++cnt].v=v;e[cnt].w=w;e[cnt].next=head[u];
        head[u]=cnt;
    }
    void fsd(int u,int fa){     //边权转化为点权
        for(int i=head[u];~i;i=e[i].next){
            int v=e[i].v;ll w=e[i].w;
            if(v==fa) continue;
            a[v]=w;p[(i-1)/2+1]=v;
            fsd(v,u);
        }
    }
    void dfs(int u,int fa,int d){
        sz[u]=1;f[u]=fa;son[u]=-1;dep[u]=d;
        for(int i=head[u];~i;i=e[i].next){
            int v=e[i].v;
            if(v==fa) continue;
            dfs(v,u,d+1);
            sz[u]+=sz[v];
            if(son[u]==-1||sz[v]>sz[son[u]]) son[u]=v;
        }
    }
    void dfs1(int u,int t){
        id[u]=++tot;
        rnk[tot]=u;
        top[u]=t;
        if(son[u]==-1) return;
        dfs1(son[u],t);
        for(int i=head[u];~i;i=e[i].next){
            int v=e[i].v;
            if(v==f[u]||v==son[u]) continue;
            dfs1(v,v);
        }
    }
    void Pushup(int rt){     //维护该点的最大最小值
        tree[rt].mx=max(tree[rt<<1].mx,tree[rt<<1|1].mx);
        tree[rt].mn=min(tree[rt<<1].mn,tree[rt<<1|1].mn);
    }
    void Pushdown(int rt){
        if(tree[rt].lazy){
            int v=tree[rt].lazy;
            tree[rt].lazy=0;
            if(v%2==0)return;     //如果 lazy是偶数,那么就没必要 将lazy 下放,因为对区间进行偶数次取反,相当于没有进行操作
    
            tree[rt<<1].mn*=-1;tree[rt<<1].mx*=-1;      //将左,右子树的mn,mx取反   
            tree[rt<<1|1].mn*=-1;tree[rt<<1|1].mx*=-1;
    
            swap(tree[rt<<1].mn,tree[rt<<1].mx);       //交换mx,mn
            swap(tree[rt<<1|1].mx,tree[rt<<1|1].mn);
            tree[rt<<1].lazy+=v;tree[rt<<1|1].lazy+=v;  //将父节点的lazy传递给子节点
        }
    }
    void build(int l,int r,int rt){
        tree[rt].mx=-INF,tree[rt].mn=INF;tree[rt].lazy=0;     //注意最大最小值都要记录,因为一旦取反,最小值就会变成最大值
        if(l==r){
            tree[rt].mx=tree[rt].mn=a[rnk[l]];
            return;
        }
        int mid=(l+r)>>1;
        build(Lson);
        build(Rson);
        Pushup(rt);
    }
    void update(int L,int R,int l,int r,int rt){       //线段树上区间修改
        if(L<=l&&r<=R){
            tree[rt].lazy++;      //这里来判断区间数值是否需要取是用 lazy%2 == 1 来判断的
            tree[rt].mx*=-1;tree[rt].mn*=-1;       //因为区间取反,所以mx和mn都 *-1
            swap(tree[rt].mx,tree[rt].mn);    //因为*-1,所以交换最大最小值
            return ;
        }
        Pushdown(rt);
        int mid=(l+r)>>1;
        if(L<=mid) update(L,R,Lson);
        if(R>mid) update(L,R,Rson);
        Pushup(rt);
    }
    void change(int l,int r,int rt,int loc,ll v){      //单点修改
        if(l==r){
            tree[rt].mx=tree[rt].mn=v;     //将叶子节点的mx,mn都置为v
            return;
        }
        Pushdown(rt);
        int mid=(l+r)>>1;
        if(loc<=mid) change(Lson,loc,v);
        else change(Rson,loc,v);
        Pushup(rt);
    }
    ll query(int L,int R,int l,int r,int rt){     //区间查询最大值
        if(L<=l&&r<=R){
            return tree[rt].mx;
        }
        Pushdown(rt);
        int mid=(l+r)>>1;
        ll res=-INF;
        if(L<=mid) res=max(res,query(L,R,Lson));
        if(R>mid) res=max(res,query(L,R,Rson));
        return res; 
    }
    void updates(int x,int y){       //修改树上区间
        int fx=top[x],fy=top[y];
        while(fx!=fy){
            if(dep[fx]>dep[fy]){
                update(id[fx],id[x],1,n,1);     //修改线段树上对应区间
                x=f[fx],fx=top[x];
            }
            else{
                update(id[fy],id[y],1,n,1);
                y=f[fy],fy=top[y];
            }
        }
        if(x==y) return;
        if(dep[x]<dep[y])
            update(id[son[x]],id[y],1,n,1);
        else
            update(id[son[y]],id[x],1,n,1);
    }
    ll sum(int x,int y){             //查询树上区间最大值
        int fx=top[x],fy=top[y];ll res=-INF;
        while(fx!=fy){
            if(dep[fx]>dep[fy]){
                res=max(res,query(id[fx],id[x],1,n,1));
                x=f[fx],fx=top[x];
            }
            else{
                res=max(res,query(id[fy],id[y],1,n,1));
                y=f[fy],fy=top[y];
            }
        }
        if(x==y) return res;
        if(dep[x]<dep[y])
            res=max(res,query(id[son[x]],id[y],1,n,1));
        else
            res=max(res,query(id[son[y]],id[x],1,n,1));
        return res;
    }
    
    int main(){
        scanf("%d",&T);
        while(T--){
            init();
            scanf("%d",&n);
            for(int i=1;i<n;i++){
                int u,v;ll w;
                scanf("%d%d%lld",&u,&v,&w);
                add(u,v,w);add(v,u,w);
            }
    
            fsd(1,-1);a[1]=0;
            dfs(1,-1,1);
            dfs1(1,1);
            build(1,n,1);
            while(true){
                scanf("%s",s);
                if(s[0]=='D') break;
                if(s[0]=='C'){
                    int a;ll b;
                    scanf("%d%lld",&a,&b);    //单点修改
                    change(1,n,1,id[p[a]],b);     //线段树上单点修改
                }
                if(s[0]=='N'){
                    int a,b;
                    scanf("%d%d",&a,&b);
                    updates(a,b);      //对树上的区间进行修改
                }
                if(s[0]=='Q'){
                    int a,b;
                    scanf("%d%d",&a,&b);
                    printf("%lld
    ",sum(a,b));     //对树上的区间查询
                }
            }
        }
        return 0;
    }

      

    2018-09-11

  • 相关阅读:
    EXTJS 4.2 资料 控件之checkboxgroup的用法(静态数据)
    EXTJS 4.2 资料 控件之Window窗体相关属性的用法
    EXTJS 4.2 资料 控件之textfield文本框加事件的用法
    Entity Framework 学习笔记(一)之数据模型 数据库
    EXTJS 4.2 资料 控件之checkboxgroup的用法(动态数据)
    EXTJS 4.2 资料 控件之Grid 列鼠标悬停提示
    Entity Framework 学习笔记(二)之数据模型 Model 使用过程
    EXTJS 4.2 资料 控件之radiogroup 的用法
    EXTJS API
    vue移动端弹框组件,vue-layer-mobile
  • 原文地址:https://www.cnblogs.com/00isok/p/9628881.html
Copyright © 2011-2022 走看看