zoukankan      html  css  js  c++  java
  • 洛谷 P4114 Qtree1 树链剖分

    题面

    题目链接

    P4114 Qtree1

    题目描述

    给定一棵 $ n $个节点的树,有两个操作:

    CHANGE $ i $ $ t_i $ 把第 $ i $条边的边权变成 $ t_i $

    QUERY $ a $ $ b $ 输出从 $ a $ 到 $ b $ 的路径中最大的边权,当 $ a=b $ 的时候,输出 0

    输入输出格式

    输入格式:

    第一行输入一个 $ n $,表示节点个数

    第二行到第 $ n $ 行每行输入三个数,$ u_i,v_i,w_i $ ,分别表示 $ u_i,v_i $ 有一条边,边权是 $ w_i $

    第 $ n+1 $ 行开始,一共有不定数量行,每一行分别有以下三种可能

    CHANGE,QUERY同题意所述

    DONE表示输入结束

    输出格式:

    对于每个QUERY操作,输出一个数,表示 $ a $ $ b $ 之间边权最大值

    输入输出样例

    输入样例:

    3
    1 2 1
    2 3 2
    QUERY 1 2
    CHANGE 1 3
    QUERY 1 2
    DONE
    

    输出样例:

    1
    3
    3
    

    说明

    【数据范围】

    $1 leq n leq 10^5 $

    操作次数 $ leq 3*10^5 $

    $ w_i $ 和 $ t_i leq 2^{31}-1$

    说明

    【时空限制】

    1000ms,512M

    思路

    熟知的 树链剖分 是解决点权的问题的,而这一题是边权。

    由于每个点可以有多个儿子,但只有一个父亲,可以考虑将边权存储到深度较大的点的点权。这样n-1条边的边权就会对应到n-1个点上,而根节点(这里以1为根节点)的点权就是0。

    再考虑题中两个操作。

    Change

    Change操作相对简单。改变两点之间的边权,即改动两点中较深点的点权。

    Query

    Query操作略有改动。查询两点间路径中所有边的最大边权,如果查询所有经过的点是有问题的。先看图

    瞎画的图

    要查询3,4两点间所有边的最大边权,如果取点3,2,4点权的最大值,那么答案是5,显然是错误的,因为点2记录的是他与他父亲之间的边权,这是肯定不会经过的边。故在统计答案时,不能统计两点的LCA。联系树剖的基本操作(其中Q为在线段树上查询最大值)

    int Ask(int u,int v)
    {
        int ans=0;
        while(top[u]!=top[v])
        {
            if(dep[top[u]]<dep[top[v]]) swap(u,v);
            ans=max(ans,Q(1,nid[top[u]],nid[u]));
            u=fa[top[u]];
        }
        if(dep[u]>dep[v]) swap(u,v);
        ans=max(ans,Q(1,nid[u],nid[v]));
        return ans;
    }
    

    其中while循环不用改变,while循环结束后,u和v处于同一条链上。

    如果u==v,此时u和v都是最初两点的LCA,此时直接返回

    否则,u和v之中深度较小的那一点是最初两点的LCA,应该从这个点的重儿子开始统计答案。

    故改变后的代码如下

    int Ask(int u,int v)
    {
        int ans=0;
        while(top[u]!=top[v])
        {
            if(dep[top[u]]<dep[top[v]]) swap(u,v);
            ans=max(ans,Q(1,nid[top[u]],nid[u]));
            u=fa[top[u]];
        }
        if(u==v) return ans;
        if(dep[u]>dep[v]) swap(u,v);
        ans=max(ans,Q(1,nid[u]+1,nid[v]));  
        return ans;
    }
    

    AC代码

    #include<bits/stdc++.h>
    const int maxn=100010;
    using namespace std;
    
    int n,wt[maxn];
    int tot,to[maxn<<1],nxt[maxn<<1],head[maxn];
    int dep[maxn],len[maxn],fa[maxn],son[maxn]; 
    int cnt,nid[maxn],nw[maxn],top[maxn];
    struct SegmentTree
    {
        int l,r,mx;
        #define l(a) tree[a].l
        #define r(a) tree[a].r
        #define m(a) ((l(a)+r(a))>>1)
        #define mx(a) tree[a].mx
    }tree[maxn<<2];
    
    void dfs1(int u,int f,int d)
    {
        dep[u]=d;fa[u]=f;len[u]=1;
        int maxson=-1;
        for(int i=head[u];i;i=nxt[i])
        {
            int v=to[i];
            if(v==f) continue;
            dfs1(v,u,d+1);
            len[u]+=len[v];
            if(len[v]>maxson) maxson=len[v],son[u]=v;
        }
    }
    
    void dfs2(int p,int t)
    {
        nid[p]=++cnt;
        top[p]=t;
        if(!son[p]) return;
        dfs2(son[p],t);
        for(int i=head[p];i;i=nxt[i])
        {
            int v=to[i];
            if(v==fa[p] || v==son[p]) continue;
            dfs2(v,v);
        }
    }
    
    void BuildTree(int p,int l,int r)
    {
        l(p)=l;r(p)=r;
        if(l==r)
        {
            mx(p)=nw[l];
            return;
        }
        BuildTree(p<<1,l(p),m(p));
        BuildTree(p<<1|1,m(p)+1,r);
        mx(p)=max(mx(p<<1),mx(p<<1|1));
    }
    
    void Change(int np,int p,int k)
    {
        if(l(np)==r(np))
        {
            mx(np)=k;
            return;
        }
        if(p<=m(np)) Change(np<<1,p,k);
        if(p>m(np)) Change(np<<1|1,p,k);
        mx(np)=max(mx(np<<1),mx(np<<1|1));
    }
    
    int Q(int p,int l,int r)
    {
        if(l<=l(p) && r>=r(p)) return mx(p);
        int ans=0;
        if(l<=m(p)) ans=max(ans,Q(p<<1,l,r));
        if(r>m(p)) ans=max(ans,Q(p<<1|1,l,r));
        return ans;
    }
    
    int Ask(int u,int v)
    {
        int ans=0;
        while(top[u]!=top[v])
        {
            if(dep[top[u]]<dep[top[v]]) swap(u,v);
            ans=max(ans,Q(1,nid[top[u]],nid[u]));
            u=fa[top[u]];
        }
        if(u==v) return ans;
        if(dep[u]>dep[v]) swap(u,v);
        ans=max(ans,Q(1,nid[u]+1,nid[v]));
        return ans;
    }
    
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<n;i++) 
        {
            int u,v,w;scanf("%d%d%d",&u,&v,&w);wt[i]=w;
            to[++tot]=v;nxt[tot]=head[u];head[u]=tot;
            to[++tot]=u;nxt[tot]=head[v];head[v]=tot;
        }
        dfs1(1,1,1);
        dfs2(1,1);
        for(int i=1;i<n;i++)
        {
            int u=to[2*i-1],v=to[2*i];
            if(dep[u]>dep[v]) nw[nid[u]]=wt[i];
            else nw[nid[v]]=wt[i];
        }
        BuildTree(1,1,n);
        while(1)
        {
            string way;cin>>way;if(way=="DONE") break;
            if(way=="CHANGE")
            {
                int i,k;scanf("%d%d",&i,&k);
                int u=to[2*i-1],v=to[2*i];
                if(dep[u]>dep[v]) Change(1,nid[u],k);
                else Change(1,nid[v],k);
            }
            if(way=="QUERY")
            {
                int u,v;scanf("%d%d",&u,&v);
                printf("%d
    ",Ask(u,v));
            }
        }
        return 0;
    }
    

    总结

    树剖处理边权的板子题

  • 相关阅读:
    js与jquery常用数组方法总结
    js 对象深复制,创建对象和继承
    Web前端面试常识
    jQuery插件开发之boxScroll与marquee
    jQuery插件开发之windowScroll
    《将博客搬至CSDN》
    蓝桥杯 翻硬币
    AcWing 756.蛇形矩阵
    货仓选址
    费解的开关
  • 原文地址:https://www.cnblogs.com/Mercury04/p/9703318.html
Copyright © 2011-2022 走看看