• 线段树合并


    先说说启发式合并

    启发式合并可以看做是暴力的优化,一共n个元素,如果我们朴素的一个接一个合并,合并一次时间是O(n),要合并n次,时间是O(n^2)

    但如果我们每次合并的时候,选择小的合并进大的,则最多合并logn次,时间是O(nlogn)


    线段树合并

    一般是合并值域线段树

    初始有n个只有一个叶子有值的线段树,在合并两个线段树的时候对于共有的节点把信息合并。
    对于不共有的节点直接返回,则最后把所有线段树合并成一颗的复杂度是O(nlgn)(假设信息合并是O(1)的)
    证明:对于共有的部分,相当于删去了那么多点,最初有O(nlgn)个点,最后剩O(n)个点,没有新建点的操作,所以总复杂度是O(nlgn)的。

    开空间的时候要开O(logn*n)

    以下可以结合下面那道例题思考

    假设一开始所有点都是独立的,存在自己的线段树中,值域1~n,开的点就应是logn的(可以看做是一条链),n棵就是n*logn。

    int merge(int x,int y)
    {
        if(!x) return y;
        if(!y) return x;
        lc[x]=merge(lc[x],lc[y]);
        rc[x]=merge(rc[x],rc[y]);
        sum[x]=sum[lc[x]]+sum[rc[x]];
        return x;
    }
    模板

    bzoj2733永无乡可以用线段树合并解决

    #include<bits/stdc++.h>
    #define N 100003
    using namespace std;
    int read()
    {
        int x=0,f=1;char s=getchar();
        while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
        while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
        return x*f;
    }
    int ndnum=0;
    int lc[N*20],rc[N*20],sum[N*20],root[N],fa[N],import[N],id[N];
    //开的是值域线段树,每一个i一开始会建出一个1~n的值域线段树,高度logn,差不多是20 
    int getfa(int x)
    {
        if(fa[x]==x)return x;
        return fa[x]=getfa(fa[x]);
    }
    void insert(int &k,int l,int r,int val)//会修改,注意是&k(或者就用return的) 
    {
        if(!k) k=++ndnum;
        if(l==r){sum[k]++;return;}
        int mid=(l+r)>>1;
        if(val<=mid)insert(lc[k],l,mid,val);
        else insert(rc[k],mid+1,r,val);
        sum[k]=sum[lc[k]]+sum[rc[k]];
    }
    int query(int k,int l,int r,int rank)
    {
        if(l==r)return l;
        int mid=(l+r)>>1;
        if(rank<=sum[lc[k]])return query(lc[k],l,mid,rank);
        else 
        {
            rank-=sum[lc[k]];
            return query(rc[k],mid+1,r,rank);
        }
    }
    int merge(int x,int y)
    {
        if(!x) return y;
        if(!y) return x;
        lc[x]=merge(lc[x],lc[y]);
        rc[x]=merge(rc[x],rc[y]);
        sum[x]=sum[lc[x]]+sum[rc[x]];
        return x;
    }
    char op[2];
    int main()
    {
        int n=read(),m=read();
        for(int i=1;i<=n;++i)
          fa[i]=i;
        for(int i=1;i<=n;++i)
          import[i]=read(),id[import[i]]=i;
        for(int i=1;i<=m;++i)
        {
            int a=read(),b=read();
            int s1=getfa(a),s2=getfa(b);
            fa[s2]=s1;
        }
        for(int i=1;i<=n;++i)
        {
            int belong=getfa(i);
            insert(root[belong],1,n,import[i]);
        }
        int q=read();
        while(q--)
        {
            scanf("%s",op);
            int x=read(),y=read();
            if(op[0]=='B')
            {
                int s1=getfa(x),s2=getfa(y);
                if(s1==s2)continue;
                fa[s2]=s1;
                root[s1]=merge(root[s1],root[s2]);
            }
            else
            {
                int belong=getfa(x);
                if(sum[root[belong]]<y){printf("-1
    ");continue;}
                int p=query(root[belong],1,n,y);
                printf("%d
    ",id[p]);
            }
        }
    } 
    bzoj2733永无乡

    线段树启发式合并

    其实这个可能都不会怎么用到吧!毕竟时间是O(n*logn*logn)的,比递归式线段树合并多了一个log呢!

    初始有n个只有一个叶子有值的线段树,在合并两个线段树的时候对于共有的节点把信息合并。
    将含有数较少的线段数中的所有数插入另一个线段树,则最后把所有线段树合并成一颗的复杂度是O(nlognlogn)(假设提取数、信息合并是O(1)的)
    证明:最糟糕的情况即完全二叉树,但SPLAY如此合并是O(nlogn)的只是常数很大

  • 相关阅读:
    vs15
    Areas(区域)
    池编程技术
    MyBitis(iBitis)系列随笔之五:多表(一对多关联查询)
    MyBatis学习总结(五)——实现关联表查询
    Mapper映射语句高阶应用——ResultMap
    mysql中的null字段值的处理及大小写问题
    spring-boot支持双数据源mysql+mongo
    No resource identifier found for attribute 'showAsAction' in package 'android'
    No resource found that matches the given name 'Theme.AppCompat.Light'.
  • 原文地址:https://www.cnblogs.com/yyys-/p/11234520.html
Copyright © 2020-2023  润新知