zoukankan      html  css  js  c++  java
  • 分块入门学习

    学习博客:http://hzwer.com/8053.html

    分块,顾名思义,就是把很长的一整个区间,分成几个小的区间去处理,降低时间复杂度。(其实就和莫队思想差不多)

    那么我们究竟分成多少块呢?答案是n−−√n。

    因为这样我们分出来的每一段区间的数字个数就是n−−√n。

    区间个数和每个区间所维护的数字个数相等,所以时间复杂度是比较优的。

    比如n=100的时候,分成了10个区间,每个区间维护10个数。
    有人问n=105的时候怎么办,那我们就多分一个区间,共11个区间,前10个区间每个区间维护10个数,第11个区间维护5个数就好了(换句话说就是余数单独分一个区间,并且可知余数不超过n−−√n)。

    我们先看一道裸题。

    初始时给你n个数字(n<=100000n<=100000),至多q次操作(q<=100000q<=100000),操作分为两种:
    1 x y表示将第x个数字加上y
    2 x y表示求[x,y]区间的最大值

    有人一看卧槽这不是线段树或树状数组裸题吗?是的,线段树和树状数组完全可以做,但是分块怎么做呢?

    可能大家一下子就想出来了,我们将n个数字分成n−−√n个区间之后,每个区间维护一个最大值,在第一种操作的时候,我们修改一下单点的值并维护所在的区间,在第二种操作时,我们扫描一遍从x到y的区间,注意,我们并不是暴力地扫描每一个点,而是如果某个区间被包括在[x,y]的范围内,我们就直接看区间维护的最大值就可以了,而对于只有一部分在[x,y]范围内的区间,我们就暴力看每个数就行了。

    我们可以推出这样做时间复杂度是nn−−√nn
    为什么呢,单点更新的时候,显然是O(1)O(1)
    而查询的时候,对于全部包含在[x,y]中的区间,我们至多需要查看n−−√n个区间,而对于不完全被包含在[x,y]中的区间,我们暴力地去查看,也只需要看至多两个区间(左端点和右端点所在的区间),共2(n−−√−1)2(n−1)个数,所以总的时间复杂度还是n−−√n
    nn次操作,那就是O(nn−−√)O(nn)
    我们可以看到这个时间复杂度是不如线段树O(nlogn)O(nlogn)的,但是也是可以接受,并且能过很多题的。
      

    #include <iostream>
    #include <math.h>
    #include <string.h>
    #include <stdio.h>
    #include <algorithm>
    
    using namespace std;
    const int maxn = 100050;
    int n,q,block,l[maxn],r[maxn],num,belong[maxn],Max[maxn];
    //block表示每个区间所维护的数的个数,num表示区间个数
    //l[i]和r[i]表示第i个区间的左右端点,belong[i]表示第i个值所在的区间
    //Max[i]表示第i个区间的最大值
    int a[maxn];
    void build()
    {
        int block=sqrt(n);
        int num=n/block;
        if(n%block)//如果有余数则需要另开一个区间
            num++;
        for(int i=1;i<=num;i++)
        {
            l[i]=(i-1)*block+1;
            r[i]=i*block;
        }
        r[num]=n;//最后一个区间右端点特殊处理
        memset(Max,0,sizeof(Max));
        for(int i=1;i<=n;i++)//初始化Max数组
        {
            belong[i]=(i-1)/block+1;
            Max[belong[i]]=max(Max[belong[i]],a[i]);
        }
    }
    
    void update(int pos,int y)
    {
        a[pos]+=y;
        Max[belong[pos]]=max(Max[belong[pos]],a[pos]);
    }
    int query(int x,int y)
    {
        int ll=belong[x];
        int rr=belong[y];
        if(ll==rr)//x和y在同一个区间的情况
        {
            int ans=0;
            for(int i=x;i<=y;i++)
                ans=max(ans,a[i]);
            return ans;
        }
        else
        {
            int ans=0;
            for(int i=x;i<=r[ll];i++)//处理x所在的区间
                ans=max(ans,a[i]);
            for(int i=ll+1;i<=rr-1;i++)//中间的区间
                ans=max(ans,Max[i]);
            for(int i=l[rr];i<=y;i++)//处理y所在的区间
                ans=max(ans,a[i]);
            return ans;
        }
    }
    int main()
    {
        scanf("%d%d",&n,&q);
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        build();
        while(q--)
        {
            int t,x,y;
            scanf("%d%d%d",&t,&x,&y);
            if(t==1)
                update(x,y);
            else
                printf("%d
    ",query(x,y));
        }
        return 0;
    }
    当初的梦想实现了吗,事到如今只好放弃吗~
  • 相关阅读:
    9.13 h5日记
    9.12 h5日记
    9.11 h5日记
    9.10 h5日记
    H5笔记周记
    ASP.NET-GridView之表头设计
    论执行力
    BS总结篇­
    花样年纪的记录(一)
    Nginx+ISS+Redis实现完美负载均衡
  • 原文地址:https://www.cnblogs.com/caijiaming/p/10304423.html
Copyright © 2011-2022 走看看