【POJ 3468】A Simple Problem with Integers【分块】

题目大意:

题目链接:http://poj.org/problem?id=3468
给出一个初始数列,有两种操作:

  • Q x yQ\ x\ y,输出这个数列xxyy之间的数字和。
  • C x y zC\ x\ y\ z,将这个数列的xxyy之间的数加上zz

思路:

线段树方法
树状数组方法

这道题还可以用分块做。。。
模板的分块,就不多解释了。
把这个数列分成n\sqrt{n}块,每块长度不超过n\sqrt{n},每块有如下几个变量:

  • sum[x]sum[x]表示第xx块里面所有数的总和
  • add[x]add[x]表示将第xx块整体加或减时的值
  • L[x]L[x]表示第xx块的最左边的位置
  • R[x]R[x]表示第xx块的最右边的位置

每一块都有一些关于数字的变量,那么每一个数字也有它的那一块的变量:

  • a[i]a[i]不用解释了。。。读入的初始值
  • pos[i]pos[i]数字ii属于第几块

过程详见代码:


代码:

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iostream>
#define N 100011
#define ll long long
using namespace std;

int n,m,L[N],R[N],pos[N],x,y,t;
ll add[N],sum[N],a[N],z;
char c;

ll ask(int l,int r)  //查找
{
	int q=pos[l],p=pos[r];  //左右边界分别的块
	ll ans=0;
	if (q==p)  //在同一块就直接暴力求答案(反正不会超过sqrt(n))
	{
		for (int i=l;i<=r;i++)
		 ans+=a[i]+add[q];  //a[i]是原来的答案,add[q]是整体加的时候这一块加的和。
		return ans;
	} 
	//不在同一块,就将中间的包含了整块整块的直接求出来
	//然后在将左右两边用上述暴力方法求出
	for (int i=q+1;i<p;i++)
	 ans+=add[i]*(ll)(R[i]-L[i]+1)+sum[i];  //中间整块的答案
	for (int i=l;i<=R[q];i++)
	 ans+=a[i]+add[q];  //暴力左边
	for (int i=L[p];i<=r;i++)
	 ans+=a[i]+add[p];  //暴力右边
	return ans;
}

void Add(int l,int r,ll z) //修改
{
	int q=pos[l],p=pos[r];
	if (q==p)  //在同一块就直接暴力
	{
		for (int i=l;i<=r;i++)
		{
			a[i]+=z;  //直接暴力加上
			sum[q]+=z;  //这一块的总和也要加
		}
		return;
	}
	//还是像查找那样,先求出中间的整段的答案,两边在分开处理
	for (int i=q+1;i<p;i++) 
	 add[i]+=z;  //中间整段都加上z
	for (int i=l;i<=R[q];i++)  //左边
	{
		a[i]+=z;
		sum[q]+=z;
	}
	for (int i=L[p];i<=r;i++)  //右边
	{
		a[i]+=z;
		sum[p]+=z;
	}
	return;
}

int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
	 scanf("%lld",&a[i]);
	t=(int)sqrt(n);  //总块数
	for (int i=1;i<=t;i++)
	{
		L[i]=(i-1)*t+1;
		R[i]=i*t;  //求出每一快的长度和左右边界
		for (int j=L[i];j<=R[i];j++)
		{
			pos[j]=i;
			sum[i]+=a[j];  //初始化
		}
	}
	if (R[t]<n)  //有可能还会差一块,补上
	{
		L[++t]=R[t-1]+1;
		R[t]=n;
		for (int i=L[t];i<=R[t];i++)
		{
			pos[i]=t;
			sum[t]+=a[i];
		}
	}
	while (m--)
	{
		cin>>c;
		scanf("%d%d",&x,&y);
		if (c=='Q')
		{
			cout<<ask(x,y)<<endl;
		}
		else 
		{
			scanf("%lld",&z);
			Add(x,y,z);
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998636.html