洛谷P5113 Sabbat of the witch

题面

题解

分块,考虑用 vector 记下单点和整块被赋值的所有情况,同时对整块实时维护 ans。

Part I. 块的构建/重构

首先按照最后被修改的时间将块内的所有元素排序(注意如果要保证复杂度严格是 \(\mathcal O(n \sqrt n)\) 的话要用基数排序),维护当前的顺序信息,记录元素值的后缀和、当前最后一个整块赋值操作的时间 \(\mathrm {lst}_i\) 和指针 \(\mathrm {pos}_i\) 用来维护最后一个单点修改时间比整块修改时间小的位置,这样的话,整块的答案就是前 \(\mathrm{pos}_i\) 个都是 \(\mathrm{lst}_i\),而剩下的就是一个后缀和。

Part II. 区间赋值

散块暴力加了之后重构,整块直接更新答案。

Part III. 撤销某一操作

对当前操作打上 deleted 标记,弹出每个点和整块的 vector 的末尾直到末尾不再有标记,之后对散块重构。

而对于整块,如果最后的整体赋值时间比上次重构时的 \(\mathrm{lst}\) 要更大,说明整块赋值操作没有被撤销完,可以直接知道答案。

否则可以知道删掉的操作一定是单调的,可以直接通过移动 \(\mathrm{pos}\) 指针计算出答案。

总时间复杂度 \(\mathcal O(n \sqrt n)\)

代码

#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)

inline int read()
{
	int data = 0, w = 1; char ch = getchar();
	while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
	if (ch == '-') w = -1, ch = getchar();
	while (ch >= '0' && ch <= '9') data = data * 10 + (ch ^ 48), ch = getchar();
	return data * w;
}

using ll = long long;
const int N(1e5 + 10), sqrtN(500);
struct node { int l, r, v; } q[N];
int n, m, Len, a[N], del[N], bel[N], lst[sqrtN], pos[sqrtN], L[sqrtN], R[sqrtN];
ll ans[sqrtN];
std::vector<int> v[N], op[sqrtN];
std::vector<int> num[sqrtN];
std::vector<ll> sum[sqrtN];

void Radix_Sort(const int &n, std::pair<int, int> *a)
{
	static int r1[256], r2[256];
	static std::pair<int, int> b[sqrtN];
	std::pair<int, int> *j, *tar; int i, tmp;
	std::memset(r1, 0, sizeof r1), std::memset(r2, 0, sizeof r2);
	for (j = a + 1, tar = a + n + 1; j != tar; j++)
		tmp = j -> first, ++r1[tmp & 0xff], ++r2[(tmp >> 8) & 0xff];
	for (i = 1; i < 256; i++) r1[i] += r1[i - 1], r2[i] += r2[i - 1];
	for (j = a + n; j != a; j--) b[r1[(j -> first) & 0xff]--] = *j;
	for (j = b + n; j != b; j--) a[r2[((j -> first) >> 8) & 0xff]--] = *j;
}

#define tim(i) (v[i].empty() ? 0 : v[i].back())
#define val(i) (v[i].empty() ? a[i] : q[v[i].back()].v)
void rebuild(int id)
{
	static std::pair<int, int> g[sqrtN]; int l = L[id], r = R[id], c = 0;
	for (int i = l; i <= r; i++) g[++c] = std::make_pair(tim(i), i);
	Radix_Sort(c, g), num[id].resize(c), sum[id].resize(c + 1);
	for (int i = c - 1; i >= 0; i--)
		num[id][i] = g[i + 1].second, sum[id][i] = sum[id][i + 1] + val(g[i + 1].second);
	lst[id] = op[id].empty() ? 0 : op[id].back();
	for (pos[id] = 0; pos[id] < c; pos[id]++) if (g[pos[id] + 1].first >= lst[id]) break;
	--pos[id], ans[id] = 1ll * (pos[id] + 1) * q[lst[id]].v + sum[id][pos[id] + 1];
}

void Cover(int id)
{
	int l = q[id].l, r = q[id].r;
	if (bel[l] == bel[r]) { for (int i = l; i <= r; i++) v[i].push_back(id); return rebuild(bel[l]); }
	for (int i = l; bel[i] == bel[l]; i++) v[i].push_back(id); rebuild(bel[l]);
	for (int i = r; bel[i] == bel[r]; i--) v[i].push_back(id); rebuild(bel[r]);
	for (int i = bel[l] + 1; i < bel[r]; i++)
		op[i].push_back(id), ans[i] = 1ll * (R[i] - L[i] + 1) * q[id].v;
}

ll Sum(int l, int r)
{
	ll ans = 0;
#define do_node(x) ans += (tim(x) >= t ? val(x) : q[t].v)
#define tblk(i) (op[i].empty() ? 0 : op[i].back())
	if (bel[l] == bel[r]) { for (int i = l, t = tblk(bel[i]); i <= r; i++) do_node(i); return ans; }
	for (int i = l, t = tblk(bel[i]); bel[i] == bel[l]; i++) do_node(i);
	for (int i = r, t = tblk(bel[i]); bel[i] == bel[r]; i--) do_node(i);
	for (int i = bel[l] + 1; i < bel[r]; i++) ans += ::ans[i];
#undef tblk
#undef do_node
	return ans;
}

void Undo(int id)
{
	int l = q[id].l, r = q[id].r; del[id] = 1;
#define do_node(x) while (!v[x].empty() && del[v[x].back()]) v[x].pop_back()
	if (bel[l] == bel[r]) { for (int i = l; i <= r; i++) do_node(i); return rebuild(bel[l]); }
	for (int i = l; bel[i] == bel[l]; i++) do_node(i); rebuild(bel[l]);
	for (int i = r; bel[i] == bel[r]; i--) do_node(i); rebuild(bel[r]);
	for (int i = bel[l] + 1; i < bel[r]; i++)
	{
		while (!op[i].empty() && del[op[i].back()]) op[i].pop_back();
		int t = op[i].empty() ? 0 : op[i].back();
		if (t > lst[i]) ans[i] = 1ll * (R[i] - L[i] + 1) * q[t].v;
		else
		{
			while (pos[i] >= 0 && tim(num[i][pos[i]]) >= t) --pos[i];
			ans[i] = 1ll * (pos[i] + 1) * q[t].v + sum[i][pos[i] + 1];
		}
	}
#undef do_node
}

int main()
{
#ifndef ONLINE_JUDGE
	file(cpp);
#endif
	Len = std::sqrt(n = read()), m = read();
	for (int i = 1; i <= n; i++) a[i] = read();
	for (int i = 1; i <= n; i++) bel[i] = (i - 1) / Len + 1;
	for (int i = 1; i <= n; i++) R[bel[i]] = i;
	for (int i = n; i; i--) L[bel[i]] = i;
	for (int i = 1; i <= bel[n]; i++) rebuild(i);
	int cnt = 0; ll ans = 0;
	while (m--)
	{
		int op = read(), l, r;
		if (op == 1) q[++cnt] = (node) {read() ^ ans, read() ^ ans, read()}, Cover(cnt);
		else if (op == 2) l = read() ^ ans, r = read() ^ ans, printf("%lld\n", ans = Sum(l, r));
		else Undo(read() ^ ans);
	}
	return 0;
}
posted @ 2021-02-19 11:42  xgzc  阅读(139)  评论(0编辑  收藏  举报