zoukankan      html  css  js  c++  java
  • 分块分段

    简介:~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

      各类乱七八糟数据结构的本质:定义若干正则集合,并将他们组织成某种合适的结构,而查找算法就是要把查找的结果表示成若干个正则集合的划分,进而在每个正则集合中通过枚举的方式实现查找。

      分块其实就是一种最简单的组织形式——hash

      对于一个待处理的区间N,将其等分为 Size = sqrt ( N ) 块。对应每块大小则为Size。

      定其在块中的表现形式为 ( i , j ),其中i为块编号,用于指定其在哪块。j为块中序号,用于指定其在该块中的具体位置。

      易得原序号为id的数对应hash后为( id/Size , id%Size) PS:神似于计算机内存的分块

    应用1:~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

      处理对象:指定[ l , r ]区间,求最值

      [ 0 , Size ) [ Size , 2Size )....[ xSize , (x+1)Size )...

       l属于 [ Size , 2Size ) ; r属于 [ xSize , (x+1)Size )

      思路:

      预处理每块内部的最值

      遍历包含于[ l ,r ] 区间所有块,即对应块编号2—(x-1),获取比较最值

      暴力搜索[ l , r ] 两端的数值,即对应原id区间:[ l , 2Size ) &[ xSize , r]

      易证:

      预处理复杂度O(N) ;

       查询复杂度O(sqrt(N))

    //预处理:
    #define maxn 10005
    int num[maxn];
    int maxnum[Size+5];
    int n,Size;
    void init()
    {    
        for(int i=0;i<n;i++)
            if(i%Size==0 || num[i]>maxnum[i/Size] ) 
                max[i/Size]=num[i];    
    }
    //查询:
    int query(int l,int r) 
    {
        int ret=num[l];
        for(int j=l;j<=r;)
        {
            if(j%Size==0 && j+Size-1<=r) 
            {
                if(maxnum[j/Size]>ret) ret=maxnum[j/Size];
                j+=Size;
            }
            else 
            {
                if(num[j]>ret) ret=num[j];
                j++;
            }
        }
        return ret;
    }

    应用2:~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

      处理对象:在应用1的基础上添加更新功能,可修改任意点的值

      思路: 修改该值后同步更新改点所在的块内数据即可 其他同应用1

      更新复杂度O( 1 )

      代码:省略~

    应用3:~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

      应用对象:区间第K大

      思路:

      对每个块进行内部排序

      二分分界限值X,对指定区间进行查找<X的数的个数,如区间包含整块则二分,否则暴力。

      直到X满足指定条件(即所得<X的值的个数=K)

      复杂度:log( r-l ) × (N/Size x log(Size)+Size×2)

      即:log(N) sqrt(N)左右,常数略大

      代码:省略~

      按之前的理论,复杂度为:log( r-l ) × (N/Size x log(Size)+Size×2)

      其中(N/Size x log(Size)+Size×2) 部分

      Size的值不可能过大或过小,只能在sqrt(N)左右不定,否则都有极端数据能卡掉,所以这部分的复杂度平均为sqrt(N)

      如果查询次数过多,分分钟T掉。得优化!

      假想,如果每次查询的边界恰好为我Size的区间端点,我可直接二分查找,直接去掉两端的暴力过程。

      由此想到将区间进行分段,即分作长度不同的块,可对于不同的数据,最优的分段方法就不一定相同。我们最好可将所有分段方法保留下来,但这么搞内存显然不够。

      一个数必定可表示为2进制,对应2进制数的长度也不会太长。

      由此联想,对于任意一段区间,都可拆分成多项大小为2的次幂的块。

      我们只需预处理出所有大小为2的次幂的块,每次查询就是在各块内部进行二分查找。

      根据输入数据大小,得到足够多的层,每层对应一种固定大小的块。 各块内部自行排序之后即可。

      总体复杂度:

      预处理:层数 * Nlog(N)

      查询:log(N)^2

      POJ 2104:

    #include <stdio.h>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    #define maxn 100005
    const int X = 18;
    int n,m;
    int arr[maxn];
    int sorted[20][maxn];
    int countfind(int x,int l,int r,int val)
    {// 找出第x层,区间为l,r的块中有多少数 < val
        int *sorted = ::sorted[x];
        if(sorted[l]>=val) return 0;
        if(sorted[r]<val) return r-l+1;
        int st=l;
        while(l+1<r)
        {
            int mid=(l+r)>>1;
            if(sorted[mid]<val) l=mid;
            else r=mid;
        }
        return r-st;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++) scanf("%d",&arr[i]);
    
        for(int j=0;j<X;j++)
        for(int i=0;i<n;i++)
        sorted[j][i]=arr[i];
    // 预处理每层大小为 2,4,8,16... 的块
        for(int j=1;j<X;j++)
        {
            int step=1<<j;
            for(int i=0;i+step-1<n;i+=step)
            sort(sorted[j]+i,sorted[j]+i+step);
        }
        while(m--)
        {
            int l,r,k;
            scanf("%d%d%d",&l,&r,&k);
            l--;
            r--;
            int mi=-1e9-1;
            int ma=1e9+1;
            while(mi+1<ma)
            {
                int ans=0;
                int mid=(mi+ma)>>1;
                for(int i=l;i<=r;)
                {//此处相当于将一个数分成2的幂的和
                    for(int j=X;j>=0;j--)
                    {
                        int step=1<<j;
                        if(i%step==0 && i+step-1<=r)
                        {
                            ans+=countfind(j,i,i+step-1,mid);
                            i+=step;
                            break;
                        }
                    }
                }
                if(ans<k) mi=mid;
                else ma=mid;
            }
            printf("%d
    ",mi);
        }
        return 0;
    }

    修改自神秘链接:http://www.cnblogs.com/sweetsc/archive/2012/08/15/2639395.html

  • 相关阅读:
    ASP.NET Core: What I learned!
    Entity Framework Core with GraphQL and SQL Server using HotChocolate
    Angular 9 Chart.js with NG2-Charts Demo
    POST调用WCF方法-项目实践
    项目实战-登录速度优化笔记
    MP4视频流base64数据转成Blob对象
    使用Vue+ElementUI实现前端分页
    JS端实现图片、视频时直接下载而不是打开预览
    Dynamic CRM工作流流程实战
    Dynamic CRM插件调试与单元测试
  • 原文地址:https://www.cnblogs.com/mochenmochen/p/5203287.html
Copyright © 2011-2022 走看看