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

  • 相关阅读:
    leetcode 268. Missing Number
    leetcode 189. Rotate Array
    leetcode 118. Pascal's Triangle 、119. Pascal's Triangle II 、120. Triangle
    HDU高精度总结(java大数类)
    一起talk C栗子吧(第八十七回:C语言实例--使用管道进行进程间通信概述)
    iOS敏捷开发之道,经常使用的宏定义总结
    360 网络攻防 hackgame 解题报告(通关)
    打通B/S与C/S !让HTML5 WebSocket与.NET Socket公用同一个服务端!
    数列求和
    指尖上的电商---(11)Windows平台部署SolrCloud
  • 原文地址:https://www.cnblogs.com/00isok/p/9628881.html
Copyright © 2011-2022 走看看