分块

写法一:懒!!!!

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
int n, m;
long long a[100005];
long long blng[100005];  // blng[i]代表i这个数是第几块
long long sum[100005];   // sum[i]代表第i块所有数的和
long long delta[100005]; // delta[i]代表第i块整体被加了多少
int siz[100005];        // siz[i]代表第i块有多少个数
int main()
{
  scanf("%d %d", &n, &m); // 读入数的个数和操作数量
  for (int i = 1; i <= n; i++)
  {
    scanf("%lld", &a[i]);
  }
  int s = sqrt(n); // s为一个块的大小
  for (int i = 1; i <= n; i++)
  {
    blng[i] = (i - 1) / s + 1;
  }
  for (int i = 1; i <= n; i++)
  {
    sum[blng[i]] += a[i]; // 看题目是否要求取模(%mod)
    siz[blng[i]]++;
  }
  for (int i = 1; i <= m; i++)
  {
    int op;
    scanf("%d", &op);
    if (op == 1)
    { // 修改操作
      int l, r, v;
      scanf("%d %d %d", &l, &r, &v); // 要把a[l]~a[r]全部 +v
      if (blng[l] == blng[r])
      { // 特殊情况,操作范围的左端点、右端点同属一个块内
        for (int j = l; j <= r; j++)
        {
          a[j] += v;
          sum[blng[j]] += v;
        }
      }
      else
      {
        for (int j = l; blng[j] == blng[l]; j++)
        {
          a[j] += v;
          sum[blng[j]] += v;
        }
        for (int j = blng[l] + 1; j < blng[r]; j++)
        {
          delta[j] += v;
          sum[j] += siz[j] * v; // 分块时无法确定每块都是均匀的s个,所以提前开数组记录长度
        }
        for (int j = r; blng[j] == blng[r]; j--)
        {
          a[j] += v;
          sum[blng[j]] += v;
        }
      }
    }
    else
    {
      int l, r, ans = 0;
      scanf("%d %d", &l, &r); // 询问a[l]~a[r]
      if (blng[l] == blng[r])
      {
        for (int j = l; j <= r; j++)
        {
          ans += (a[j] + delta[blng[j]]);
        }
      }
      else
      {
        for (int j = l; blng[j] == blng[l]; j++)
        {
          ans += (a[j] + delta[blng[j]]);
        }
        for (int j = blng[l] + 1; j < blng[r]; j++)
        {
          ans += sum[j];
        }
        for (int j = r; blng[j] == blng[r]; j--)
        {
          ans += (a[j] + delta[blng[j]]);
        }
      }
      printf("%d\n", ans);
    }
  }
  return 0;
}

写法二:(懒标记随时下放)

#include<iostream>
#include<cmath>

using namespace std;

const int maxn=100010;

int n,m,a[maxn],s;
int belong[maxn];//belong[i]代表i这个数是第几块 
int siz[maxn];//siz[i]代表第i块有几个数 
int sum[maxn];//sum[i]代表第i块所有数的和
int delta[maxn];//delta[i]代表第i块整体被加了多少 
void push(int i)//把第i块的delta应用到a上
{
	if (delta[i] == 0) return;
	int l=s*(i-1)+1; 
	for (int j=l;belong[j] == belong[l]; j++)
		a[j] += delta[i];
	delta[i]=0;
} 
int main()
{
	cin >> n >> m;//读入数的个数和操作数量
	for (int i=1;i<=n;i++)
		cin >> a[i];
	s=sqrt(n);//s是一个块的大小 
	for (int i=1;i<=n;i++)
		belong[i] = (i-1)/s+1;
	for (int i=1;i<=n;i++)
		sum[belong[i]] += a[i], siz[belong[i]] ++; 
	for (int i=1;i<=m;i++)
	{
		int opt;
		cin >> opt;
		if (opt==1)//修改操作
		{
			int l,r,v;
			cin >> l >> r >> v;//要把a[l]~a[r]全部+v
			push(l);push(r);
			if (belong[l] == belong[r])
			{
				for (int j=l;j<=r;j++)
				{
					a[j] += v;
					sum[belong[j]] += v;
				}
			}
			else
			{
				for (int j=l;belong[j] == belong[l];j++)
				{
					a[j] += v;
					sum[belong[j]] += v;
				}
				for (int j=belong[l]+1;j<belong[r];j++)
				{
					delta[j] += v;
					sum[j] += siz[j]*v;
				}
				for (int j=r;belong[j] == belong[r];j--)
				{
					a[j] += v;
					sum[belong[j]] += v;
				}
			} 
		}
		else
		{
			int l,r,ans=0;
			cin >> l >> r;//询问a[l]~a[r]	
			push(l);push(r);
			if (belong[l] == belong[r])
			{
				for (int j=l;j<=r;j++)
					ans += a[j];
			}
			else
			{
				for (int j=l;belong[j] == belong[l];j++)
					ans += a[j];
				for (int j=belong[l]+1;j<belong[r];j++)
					ans += sum[j];
				for (int j=r;belong[j] == belong[r];j--)
					ans += a[j];
			} 
			cout << ans << endl;
		}
	}
	
	 
	return 0;
}
posted @ 2024-10-02 19:05  SigmaToT  阅读(11)  评论(1)    收藏  举报