分块

分块

瞎搞个玩意支持对一个数组区间求和,单点修改。

啊,已经有很多方法了?

  • 朴素算法:O(1)修改,O(n)查询
  • 前缀和:O(n)修改,O(1)查询
  • 树状数组:O(logn)修改,O(logn)查询

看似好像齐全了……然而还是有个比朴素好的东西:分块

我们把整个数组分成sqrt(n)个部分,每个部分维护区间和和元素值。

操作:

(1)修改:直接修改:O(1)

(2)查询:对于中间的部分,直接将区间和加起来,两边零碎的朴素求法,O(sqrt(n))

啊,有了树状数组,要你何用?

好像有玄学做法……亦或者你的查询多呢,蛤蛤。

代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>

#define rep(i,x,y) for (int i=x;i<=y;i++)
#define dep(i,y,x) for (int i=y;i>=x;i--)

using namespace std;

const int maxn=100000+1000;
const double eps=0.000001;

int n,m,M,len,k,x,y,a[maxn],s[maxn];

void init() //初始化
{
 M=(int)(sqrt(n)+eps);
 if (n%M==0) len=n/M; else len=n/M+1;

 rep(i,0,M-1)
  rep(j,i*len,(i+1)*len-1) s[i]+=a[j];
}

void update(int x,int k) //修改
{
 a[x]+=k;s[x/len]+=k;
}

int query(int x,int y) //查询
{
 int ans=0;
 if ((x/len)==(y/len)) 
  while (x<=y) ans+=a[x++]; //在一个区间里,直接加
 else 
 {
  while ((x%len)>0) ans+=a[x++]; //算左边零碎的
  while (((y+1)%len)>0) ans+=a[y--]; //算右边零碎的
  while (x<y) {ans+=s[x/len];x+=len;} //算中间整的
 }  
 return ans;
}

int main()
{
 scanf("%d",&n);
 rep(i,0,n-1) scanf("%d",&a[i]);
 
 init();

 scanf("%d",&m);
 rep(i,1,m)
 {
  scanf("%d%d%d",&k,&x,&y);
  if (k==1) update(x-1,y); else printf("%d\n",query(x-1,y-1));
 }
 return 0;
}

区间修改:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>

#define rep(i,x,y) for (int i=x;i<=y;i++)
#define dep(i,y,x) for (int i=y;i>=x;i--)

using namespace std;

const int MAXN=200000+23;
const int EPS=1e-6;

typedef long long LL;

int B,n,m,k,l,r,x,a[MAXN],num[MAXN];

LL S[MAXN],Tag[MAXN];

void add(int l,int r,int v)
{
 rep(i,l,min(num[l]*B,r))
  a[i]+=v,S[num[i]]+=v;
 if (num[l]!=num[r])
  rep(i,num[r]*B-B+1,r)
   a[i]+=v,S[num[i]]+=v;
 rep(i,num[l]+1,num[r]-1)
  Tag[i]+=v;
}

LL query(int l,int r)
{
 LL ans=0;
 rep(i,l,min(num[l]*B,r)) ans+=a[i]+Tag[num[l]];
 if (num[l]!=num[r])
    rep(i,num[r]*B-B+1,r) ans+=a[i]+Tag[num[r]];
 rep(i,num[l]+1,num[r]-1)
  ans+=S[i]+B*Tag[i];
 return ans;
}

int main()
{
 scanf("%d",&n);
 
 B=(int)(sqrt(n)+EPS);

 memset(S,0,sizeof(S));
 rep(i,1,n)
 {
  scanf("%d",&a[i]);
  num[i]=(i-1)/B+1;
  S[num[i]]+=a[i];
 }

 scanf("%d",&m);

 rep(i,1,m)
 {
  scanf("%d",&k);
  if (k==1) 
  {
   scanf("%d%d%d",&l,&r,&x);
   add(l,r,x);
  }
  else 
  {
   scanf("%d%d",&l,&r);
   printf("%lld\n",query(l,r));
  }
 }

 return 0;
}
posted @ 2016-06-18 21:57  Krew  阅读(161)  评论(0)    收藏  举报