• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

RomanLin

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

【分块】LibreOJ 6280 数列分块入门4

题目

https://loj.ac/p/6280

题解

将 \(n\) 个元素的数组 \(a\) 按块长 \(\sqrt{n}\) 进行分块处理。为每个块设置两个懒添加标记 \(add[i], sum[i]\),分别代表这个区间每个元素共同添加的数值大小,区间和(不包括懒添加的值)。

对于区间加操作,将添加值存储在符合整块都进行加法操作的块的懒标记 \(add[i]\) 上,单次操作时间复杂度 \(O(1)\),此类块至多只有 \(\sqrt{n}\) 块,最差时间复杂度 \(O(\sqrt{n})\);未符合整块的进行加法操作则进行暴力处理,即为下标位于 \([l, r]\) 的元素直接添加上 \(c\),单次操作时间复杂度 \(O(\sqrt{n})\),此类块至多只有 \(2\) 块,最差时间复杂度 \(O(2\sqrt{n})\)。

对于区间和操作,将覆盖到的整块的 \(sum[i]\) 直接添加到结果中,并且添加当前块大小乘以 \(add[i]\) 到结果中。单次操作时间复杂度 \(O(1)\),此类块至多只有 \(\sqrt{n}\) 块,最差时间复杂度 \(O(\sqrt{n})\);未符合整块的进行区间和操作则进行暴力处理,单次操作时间复杂度 \(O(\sqrt{n})\),此类块至多只有 \(2\) 块,最差时间复杂度 \(O(2\sqrt{n})\)。

参考代码

#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
using namespace std;

typedef long long ll;
constexpr int N = 50007;
int n, op, l, r, c;
int len;//块长
ll a[N];//数列
ll add[230];//懒添加标记
ll sum[230];//块和

/*初始化块*/
void initPieces() {
	len = sqrt(n);
	for (int i = 1, j = 1; i <= n; ++ i) {
		sum[j] += a[i];
		if (i % len == 0) ++ j;
	}
}

/*获取下标 x 所在的块的索引*/
int getPieceId(int x) {
	return (x - 1) / len + 1;
}

/*获取第 pid 块的大小,即这个块的元素个数*/
int getPieceSize(int pid) {
	return min(n, pid * len) - (pid - 1) * len;
}

/*判断下标 x 是否为块的左边界*/
bool isLeftBoundary(int x) {
	return (x - 1) % len == 0;
}

/*判断下标 x 是否为块的右边界*/
bool isRightBoundary(int x) {
	return x % len == 0;
}

int main() {
	IOS
	cin >> n;
	for (int i = 1; i <= n; ++ i) cin >> a[i];
	initPieces();
	for (int i = 0; i < n; ++ i) {
		cin >> op >> l >> r >> c;
		bool isLe = isLeftBoundary(l), isRi = isRightBoundary(r);
		int le = getPieceId(l), ri = getPieceId(r);
		if (op) {//区间和
			++ c; 
			ll res = 0LL;
			for (int i = isLe ? le : le + 1, j = isRi ? ri : ri - 1; i <= j; ++ i) {
				res = (res + sum[i] % c + add[i] * getPieceSize(i) % c) % c;
			}
			if (!isLe) {
				while (l <= r) {
					res = (res + add[le] + a[l]) % c;
					if (isRightBoundary(l)) break;
					++ l;
				}
			}
			if (!isRi) {
				while (l <= r) {
					res = (res + add[ri] + a[r]) % c;
					if (isLeftBoundary(r)) break;
					-- r;
				}
			}
			cout << res << '\n';
		} else {//区间加
			for (int i = isLe ? le : le + 1, j = isRi ? ri : ri - 1; i <= j; ++ i) add[i] += c;
			if (!isLe) {
				while (l <= r) {
					a[l] += c;
					sum[le] += c;
					if (isRightBoundary(l)) break;
					++ l;
				}
			}
			if (!isRi) {
				while (l <= r) {
					a[r] += c;
					sum[ri] += c;
					if (isLeftBoundary(r)) break;
					-- r;
				}
			}
		}
	}
	return 0;
}

posted on 2024-11-26 23:51  RomanLin  阅读(34)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3