洛谷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;
}