分块算法
浅谈分块
区间修改,单点查询
- 众所周知,这种题线段树是可以跑得下来的,但是线段树的常数太大了,前几天本蒟蒻改一道线段树题改到自闭,调好之后就被卡掉了,太可怕了。蒟蒻就手残,打了一篇博客......
分块大法
-
对于任意一个序列{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]);
//单点查询
}
}