分块算法

分块算法:

思想:把整个序列分成若干块,s取\sqrt{n}

如果我们把一个数列,当该数列的长度为n的时候,我们以根号n为一段,分出来的段数不超过根号n,如果我们要进行区间的处理,比如加法减法等,可以对于修改区间[ L , R ]可以把其中框起来的块(一块是根号n的大小)直接打上标记,由于每一块的长度不大于根号n,所以对于两边没有框起来的部分,我们直接暴力地进行更新,这样操作次数是最多2倍根号n的,而中间的标记是O(1)处理的。

分块和线段树的区别在于,分块算法可以维护一些线段树维护不了的东西,例如单调队列等,线段树能维护的东西必须能够进行信息合并,而分块则不需要。不过,它们也有共同点,分块和线段树一样,分块需要支持类似标记合并的东西。 

//用这个题cdoj1324来阐述一下分块的操作

//简化题意: 
单点更新某个值,区间查询最大值

//代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn= 1e5+7;
int block;//每个块的大小
int belong[maxn];//某个数属于哪个块
int num;//一共有多少个块
int l[maxn];//每个块的左边界
int r[maxn];//每个块的右边界
long long int a[maxn];
long long int n,q,Max[maxn];
void build()
{
    block=sqrt(n);
    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;
    for(int i=1;i<=n;i++)
        belong[i]=(i-1)/block+1;
    for(int i=1;i<=num;i++)//这段for循环只是这个题里需要
    {
        for(int j=l[i];j<=r[i];j++)
                Max[i]=max(Max[i],a[j]);
    }
}
void update(int x,int y)
{
    a[x]+=y;
    Max[belong[x]]=max(Max[belong[x]],a[x]);
    //不仅要更新这个数,而且要更新它所在的这个块信息;
}
long long ask(int x,int y)
{
        long long ans=0;
        if(belong[x]==belong[y])//x和y在同一块里就暴力
        {
                for(int i=x;i<=y;i++)
                        ans=max(a[i],ans);
                return ans;
        }
        for(int i=x;i<=r[belong[x]];i++)
                ans=max(ans,a[i]);
        for(int i=belong[x]+1;i<belong[y];i++)
                ans=max(ans,Max[i]);
        for(int i=l[belong[y]];i<=y;i++)
                ans=max(ans,a[i]);
        return ans;
}
int main()
{
   scanf("%d%d",&n,&q);
   build();
   for(int i=1;i<=q;i++)
   {
        int op,l,r;
        scanf("%d%d%d",&op,&l,&r);
        if(op==1) update(l,r);
        else printf("%lld\n",ask(l,r));
   }
   return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_37748451/article/details/81355819
今日推荐