【题解】P6578 [Ynoi2019] 魔法少女网站
注意这个做法极度卡常,不保证不同实现可以 AC
思路
操作分块 + 普通分块。
题意将区间内的数分成两类:小于等于 \(x\) 的和大于 \(x\) 的。可以考虑一个套路:将第一类数设为 \(1\),将第二类数设为 \(0\).
那么设 \([l, r]\) 区间内极长 \(1\) 连续段的长度分别是 \(l_1, ..., l_m\),则显然答案是 \(\sum\limits_{i = 1}^m \frac{l_m (l_m + 1)}{2}\)
于是只需要考虑维护区间内极长的连续 \(1\) 段即可。
在考虑这个东西之前还有一个问题。\(x\) 不是单调的,所以要同时维护增删。增加可以直接考虑分讨,但是删除比较难做,所以要考虑令 \(x\) 一定程度上单调,这样就只需要维护一种操作。
一种朴素的想法是把所有询问离线下来按 \(x\) 排序,但是这样修改就需要考虑偏序问题。感觉上比较麻烦,但也可能有不用操作分块的做法。
所以我们希望在一定范围内令 \(x\) 单调的同时保证复杂度可过,所以可以想到类似根号分治的东西,也就是操作分块。
简单考虑,操作分块的本质实际上是在 \(O(n)\) 左右的时间完成 \(O(\sqrt{n})\) 次修改和 \(O(\sqrt{n})\) 次查询,也可以考虑成对时间分治。
一般需要考虑的贡献关系是:
-
前面的整块 -> 当前的整块
-
当前的整块 -> 当前的整块
那么只需要对操作序列进行适当地分块,然后在块内把询问排一次序,这样就可以保证询问的 \(x\) 单调。
在 \(x\) 单调的情况下,只需要考虑把 \(0\) 变成 \(1\) 就行。
维护极长连续 \(1\) 段的话,一个 \(0\) 变成 \(1\) 的贡献可以分讨出来:
-
成为某个极长 \(1\) 段的端点
-
连通位于其左右两侧的极长 \(1\) 段
考虑对于极长 \(1\) 段的端点维护其另一个端点的位置,这样合并就可以 \(O(1)\) 类似链表做。这个直接上分块就行。
因为块内修改对块内贡献需要考虑操作的先后顺序,所以不写奇怪东西的话每次查询都需要重新加入修改的贡献。
关于上面的回溯问题,因为修改的时候只会修改几个点的信息,直接记录一下原本的信息,结束之后再覆盖回去就行。这样的复杂度是块长,没有问题。
对于前面整块的贡献,因为查询的 \(x\) 单调,所以处理单块时可以考虑直接将当前序列中小于等于 \(\min x\) 的数加入贡献,结束后再根据这个块中的修改操作更改序列。
设操作分块块长为 \(B_1\),普通分块块长为 \(B_2\),则 \(B_1, B_2\) 和 \(\sqrt{n}\) 同阶时,时间复杂度大约为:
\(O(\frac{m}{B_1} (B_1 \log B_1 + B_2) + \frac{n}{B_2} B_2)\)
需要大力卡一下常:
-
科技快读快写
-
inline + register + const + &
-
信仰 cp 学号块长 \(B_1 = 1821, B_2 = 516\)
-
预处理一些常数大且常用的东西
-
卡评测机波动
代码
#include <cstdio>
#include <cmath>
#include <cstring>
// #include <vector>
#include <algorithm>
// using namespace std;
namespace IO
{
//by cyffff
int len = 0;
char ibuf[(1 << 20) + 1], *iS, *iT, out[(1 << 26) + 1];
#define gh() (iS == iT ? iT = (iS = ibuf) + fread(ibuf, 1, (1 << 20) + 1, stdin), (iS == iT ? EOF : *iS++) : *iS++)
#define reg register
inline int read()
{
reg char ch = gh();
reg int x = 0;
reg char t = 0;
while (ch < '0' || ch > '9') t |= ch == '-', ch = gh();
while (ch >= '0' && ch <= '9') x = x * 10 + (ch ^ 48), ch = gh();
return t ? -x : x;
}
inline void putc(char ch) { out[len++] = ch; }
template<class T>
inline void write(T x)
{
if (x < 0) putc('-'), x = -x;
if (x > 9) write(x / 10);
out[len++] = x % 10 + 48;
}
inline void flush()
{
fwrite(out, 1, len, stdout);
len = 0;
}
}
using IO::read;
using IO::write;
using IO::flush;
using IO::putc;
#define reg
typedef long long ll;
const int maxn = 3e5 + 1;
const int maxm = 3e5 + 1;
const int sz = 2e3 + 1;
struct Modify
{
int t, pos, val;
} q1[sz];
struct Query
{
int t, l, r, x, idx;
} q2[sz];
struct node
{
int idx, res, t[4];
bool flag;
inline void clear() { t[0] = t[1] = t[2] = t[3] = flag = 0; }
} stk[sz], tmp;
struct Edge
{
int to, nxt;
} edge[maxn];
int n, m, sqn, sqm, tot, c1, c2, top, ecnt;
int st[sz], ed[sz];
int head[maxn], a[maxn], bel[maxn], pos[maxn];
bool vis[maxn], chg[maxn], used[maxn], seq[maxn];
ll gt[maxn];
ll ans[sz], sum[sz];
// vector<int> idx[maxn];
inline int min(reg const int &a, reg const int &b) { return (a <= b ? a : b); }
inline bool cmp(const Query& x, const Query& y) { return (x.x < y.x); }
inline void modify(reg const int& p, reg bool flag)
{
pos[p] = p, seq[p] = true, tmp.idx = p;
reg const int lst = p - 1, nxt = p + 1, tmp_p = pos[lst];
reg const bool flag1 = (seq[lst] && (st[bel[p]] != p)), flag2 = (seq[nxt] && (ed[bel[p]] != p));
if ((!flag1) && (!flag2)) tmp.clear(), tmp.res = 1;
else
{
tmp.flag = true;
if (flag1 && flag2)
{
tmp.res = (nxt - pos[lst]) * (pos[nxt] - lst);
tmp.t[0] = pos[lst], tmp.t[1] = pos[pos[lst]], pos[pos[lst]] = pos[nxt];
tmp.t[2] = pos[nxt], tmp.t[3] = pos[pos[nxt]], pos[pos[nxt]] = tmp_p;
}
else if(flag1)
{
tmp.res = nxt - pos[lst];
tmp.t[0] = p, tmp.t[1] = pos[p], pos[p] = pos[lst];
tmp.t[2] = pos[lst], tmp.t[3] = pos[pos[lst]], pos[pos[lst]] = p;
}
else
{
tmp.res = pos[nxt] - lst;
tmp.t[0] = p, tmp.t[1] = pos[p], pos[p] = pos[nxt];
tmp.t[2] = pos[nxt], tmp.t[3] = pos[pos[nxt]], pos[pos[nxt]] = p;
}
}
sum[bel[p]] += tmp.res;
if (flag) stk[++top] = tmp;
}
inline ll query(const int& l, const int& r)
{
reg ll ans = 0;
if (bel[l] == bel[r])
{
reg int cnt = 0;
for (reg int i = l; i <= r; i++)
if (seq[i]) cnt++;
else ans += gt[cnt], cnt = 0;
return ans + gt[cnt];
}
reg int cnt1 = 0, cnt2 = 0;
for (reg int i = l; i <= ed[bel[l]]; i++)
if (seq[i]) cnt1++;
else ans += gt[cnt1], cnt1 = 0;
for (reg int i = r; i >= st[bel[r]]; i--)
if (seq[i]) cnt2++;
else ans += gt[cnt2], cnt2 = 0;
reg int res = cnt1;
for (reg int i = bel[l] + 1; i <= bel[r] - 1; i++)
{
if (pos[st[i]] == ed[i]) res += ed[i] - st[i] + 1;
else
{
if (seq[st[i]]) res += pos[st[i]] - st[i] + 1, ans -= gt[pos[st[i]] - st[i] + 1];
ans += gt[res] + sum[i], res = 0;
if (seq[ed[i]]) res += ed[i] - pos[ed[i]] + 1, ans -= gt[ed[i] - pos[ed[i]] + 1];
}
}
return ans + gt[res + cnt2];
}
inline void add_edge(const int& u, const int& v)
{
edge[++ecnt] = (Edge){v, head[u]};
head[u] = ecnt;
}
inline void solve()
{
memset(seq, false, sizeof(seq));
memset(pos, 0, sizeof(pos));
memset(sum, 0, sizeof(sum));
for (reg int i = 1; i <= c1; i++) chg[q1[i].pos] = true;
for (reg int i = 1; i <= n; i++)
if (!chg[i]) add_edge(a[i], i);
std::sort(q2 + 1, q2 + c2 + 1, cmp);
reg int lim = 1;
for (reg int i = 1; i <= c2; i++)
{
while (lim <= q2[i].x)
{
for (reg int& i = head[lim]; i; i = edge[i].nxt) modify(edge[i].to, 0);
lim++;
}
for (reg int j = c1; j >= 1; j--)
if ((q1[j].t < q2[i].t) && (!used[q1[j].pos]))
{
used[q1[j].pos] = true;
if (q1[j].val <= q2[i].x) modify(q1[j].pos, 1);
}
for (reg int j = 1; j <= c1; j++)
if (!used[q1[j].pos])
{
used[q1[j].pos] = true;
if (a[q1[j].pos] <= q2[i].x) modify(q1[j].pos, 1);
}
ans[q2[i].idx] = query(q2[i].l, q2[i].r);
while (top)
{
tmp = stk[top--], sum[bel[tmp.idx]] -= tmp.res, seq[tmp.idx] = false;
if (tmp.flag) pos[tmp.t[2]] = tmp.t[3], pos[tmp.t[0]] = tmp.t[1];
}
for (int j = 1; j <= c1; j++) used[q1[j].pos] = false;
}
ecnt = 0;
memset(head, 0, sizeof(head));
// for (int i = 0; i <= n; i++) idx[i].clear();
for (int i = 1; i <= c1; i++) chg[q1[i].pos] = false;
}
int main()
{
n = read(), m = read(), sqn = 516, sqm = 1821;
tot = (n + sqn - 1) / sqn;
for (reg int i = 1; i <= tot; i++) st[i] = ed[i - 1] + 1, ed[i] = (i == tot ? n : i * sqn);
for (reg int i = 1; i <= n; i++) a[i] = read(), bel[i] = (i - 1) / sqn + 1, gt[i] = 1ll * i * (i + 1) / 2;
for (reg int i = 1, j; i <= m; i = j + 1)
{
j = min(m, i + sqm), c1 = c2 = 0;
for (reg int k = i; k <= j; k++)
if (read() == 1) q1[++c1] = (Modify){k, read(), read()};
else q2[++c2] = (Query){k, read(), read(), read(), c2};
solve();
for (reg int k = 1; k <= c2; k++) write(ans[k]), putc('\n');
for (reg int k = 1; k <= c1; k++) a[q1[k].pos] = q1[k].val;
}
flush();
return 0;
}