zoukankan      html  css  js  c++  java
  • 线段树合并

      线段树合并就是把两个维护相同区间的线段树合并到一块.

      什么叫维护相同区间?就是每次操作的最大的那个区间是一样的,根节点维护的区间一样,根节点的左儿子维护的也一样,右儿子维护的也一样,左儿子的左儿子....

      显然只需要建树的时候都建成一样的就好了.一般我们采用动态开点.

      由于线段树合并的均摊复杂度是log的,虽然我看不懂证明可以开成权值线段树.

      对于两个线段树该如何合并呢?我们递归的定义:

      1.对于线段树a,b.如果只有a有当前区间或只有b有当前区间,那么把那个认定为新线段树的节点.

      2.否则我们同时递归a的左儿子和b的左儿子,递归完后把刚认定的节点作为a的左儿子.

      3.递归a的右儿子和b的右儿子,递归完后把刚认定的节点作为a的右儿子.

      4.把a认定为新线段树的节点.

      如果有两个满的线段树,每次复杂度是O(n)还多的.但是如果刚开始有n个线段树,每个线段树用动态开点维护一个数,那么合并n-1次后均摊复杂度是nlogn的.

      校内oj并没有找到例题.我口述一个例题好了.

      给你一棵有n个点的树,树上每个节点都有一种颜色 ci,求以每个点为根的子树出现最多的颜色的和.

      这是一个最简单的线段树合并了.先对每个节点的每个颜色做一个权值线段树,从根dfs整颗树.dfs完儿子后,合并父亲和儿子的权值线段树,记录答案.没了.

      口胡还是开心的.我又写了一道题以供解析.

      https://www.luogu.org/problemnew/show/P3224.

      询问的是与x相连通的所有岛中第k重要的.假如没有修改操作,可以对每个连通块弄一个权值线段树,每次询问是logn对吧.现在修改操作相当于把两个连通块联通,我们想到可以写线段树合并.

      想到连通块就会想到并查集.为了解决 "如果xy原来就联通,合并线段树又错误了"这个问题,写一个并查集即可.

      

    int n,m,tot;
    int rt[100010],fa[100010];
    struct tree
    {
        int l,r,sum,ans;
    }tr[2000010];
    int get(int x)
    {
        return fa[x]==x?x:fa[x]=get(fa[x]);
    }
    void New(int &now,int l,int r,int x,int id)
    {
        if(!now) now=++tot;
        if(l==r)
        {
            tr[now].sum++;
            tr[now].ans=id;
            return ;
        }
        int mid=l+r>>1;
        if(x<=mid)
            New(tr[now].l,l,mid,x,id);
        else 
            New(tr[now].r,mid+1,r,x,id);
        tr[now].sum=tr[tr[now].l].sum+tr[tr[now].r].sum;
    }
    int merge(int a,int b,int l,int r)
    {
        if(!a||!b) 
            return a+b;
        if(l==r)
        {
            tr[a].sum+=tr[b].sum;
            tr[a].ans=max(tr[a].ans,tr[b].ans);
            return a;
        }
        int mid=l+r>>1;
        tr[a].l=merge(tr[a].l,tr[b].l,l,mid);
        tr[a].r=merge(tr[a].r,tr[b].r,mid+1,r);
        tr[a].sum=tr[tr[a].l].sum+tr[tr[a].r].sum;
        return a;
    }
    int ask(int now,int l,int r,int x)
    {
        if(x>tr[now].sum) return -1;
        if(l==r) 
            return tr[now].ans;
        int mid=l+r>>1;
        if(tr[tr[now].l].sum>=x)
            return ask(tr[now].l,l,mid,x);
        else return ask(tr[now].r,mid+1,r,x-tr[tr[now].l].sum);
    }
    int main()
    {
        //freopen("123.in","r",stdin);
        n=read(),m=read();
        for(int i=1;i<=n;++i)
            fa[i]=i;
        for(int i=1;i<=n;++i) 
            New(rt[i],1,n,read(),i);
        for(;m;m--)
        {
            int x=get(read()),y=get(read());
            if(x==y) continue;
            else
            {
                merge(rt[x],rt[y],1,n);
                fa[y]=x;
            }
        }
        for(int Q=read();Q;Q--)
        {
            char ch;cin>>ch;
            if(ch=='Q')
            {
                int x=get(read());
                write(ask(rt[x],1,n,read()));
            }
            else
            {
                int x=get(read()),y=get(read());
                if(x==y) 
                    continue;
                else
                {
                    merge(rt[x],rt[y],1,n);
                    fa[y]=x;
                }
            }
        }
        return 0;
    }
    洛谷p3224

      个人认为线段树合并好想好写.是一个线段树思想的拓展.很有意义吧.

      

  • 相关阅读:
    谈谈 在 .Net 生态里为什么没有 Hadoop 系列 ?
    谈谈在 .Net 平台上的 软件生态 和 软件生产力
    我发起了一个 用 物理服务器 和 .Net 平台 构建云平台 的 .Net 开源项目
    推荐一个 基于 WebSocket 和 Redis 的 即时通信 开源项目
    漫谈 12306 架构
    在提交代码的时候分支上出现detached head
    地铁线路查询算法
    .NET 动态脚本语言
    Socket连接池
    比较JqGrid与XtraGrid
  • 原文地址:https://www.cnblogs.com/qywyt/p/10493626.html
Copyright © 2011-2022 走看看