分块算法

浅谈分块

区间修改,单点查询

  • 众所周知,这种题线段树是可以跑得下来的,但是线段树的常数太大了,前几天本蒟蒻改一道线段树题改到自闭,调好之后就被卡掉了,太可怕了。蒟蒻就手残,打了一篇博客......

分块大法

  • 对于任意一个序列{a},给定两种操作:

    0:给定l,r,c把l-r中a[i] (l=<i<=r)的值都加上c;

    1:给定l,r,c(l,c可以忽略)查询a[r]的值。

  • 虽然线段树数据结构是O(nlogn),分块是O(n sqrt(n))的复杂度。但是分块可以维护好多东西.

分块的实现

  • 分块就是把一个区间分成好几个小块,大部分题的复杂度都是n sqrt(n),所以默认是把区间分成sqrt(n)块。如果某题用分块复杂度带log,就让块分的更多一些,大概是乘个log。

维护

  • 当询问某段区间时,把区间覆盖的整块打上一个num标记,两边离散的块呢,就暴力就好了。最后输出时把num加上。询问区间如果在一个块里要特判,所以要多打几个if。

不废话了,请看代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,k;
struct block{
    ll val;//数值
    ll idd;//第几块
}a[50010];
ll num[50010];
inline void add(int l,int r,int c)
{
      if(a[r].idd-a[l].idd<2)
      {
          for(int i=l;i<=r;i++)
              a[i].val+=c;
          return ;
      }
      for(int i=a[l].idd+1;i<a[r].idd;i++)//区间修改
          num[i]+=c;
      for(int i=l;i<=a[l].idd*k;i++)
          a[i].val+=c;
      for(int i=(a[r].idd-1)*k+1;i<=r;i++)
          a[i].val+=c;
      return ;
}
int main()
{
      scanf("%d",&n);
      k=sqrt(n);
      for(int i=1;i<=n;i++)
      {
          scanf("%d\n",&a[i].val);
          a[i].idd=(i-1)/k+1;
      }
      for(int i=1,opt,l,r,c;i<=n;i++)
      {
          scanf("%d%d%d%d",&opt,&l,&r,&c);
          if(!opt)add(l,r,c);
          else  printf("%d\n",a[r].val+num[a[r].idd]);
                  //单点查询
      }
}
Block Code

 

posted @ 2019-07-26 19:48  milkitblogcn  阅读(236)  评论(0编辑  收藏  举报