数据结构

莫队

P5906 【模板】回滚莫队&不删除莫队

回滚莫队。

考虑离线,将询问 \(l, r\) 塞到 \(l\) 所在块中,然后对于一个块中的询问按照 \(r\) 排序,块内部分暴力算,块外部分直接移动指针。移动总次数为 \(O(n \sqrt n)\)

P5386 [Cnoi2019] 数字游戏

加入容易删除困难,考虑回滚莫队。转化为 \(x \le a_p \le y\) 的位置 \(p\)\(1\),有多少个子区间中的值全为 \(1\)

一段连续的 \(1\) 贡献为 \(\frac{len(len + 1)}{2}\)。将询问的 \((x, y)\) 看做区间,插入相当于 merge 两段连续的 \(1\)。只需维护每一个块左侧 / 右侧连续的 \(1\) 长度,每个位置左边 / 右边最远通过连续 \(1\) 能延伸到那些位置即可。

计算答案时容易的,散块暴力,整块扫一遍记录当前 \(len\) 即可。复杂度 \(O(n \sqrt n)\)

P5072 [Ynoi Easy Round 2015] 盼君勿忘

莫队。值 \(x\) 的贡献是 \(x(2^{len} - 2^{cnt_x})\)。如果模数是固定的是简单的。但每个询问的模数是变化的。考虑如果两个值的出现次数相同,他们的系数是相同的。而区间本质不同的出现次数数量是 \(O(\sqrt {len})\) 级别的。所以只需存下出现的出现次数以及其值之和即可计算。为了去掉快速幂的 \(\log\) 可以使用 \(O(\sqrt n) - O(1)\) 光速幂。复杂度 \(O(n \sqrt n)\)

P1903 [国家集训队] 数颜色 / 维护队列

带修莫队。

加一个时间维,扫到一个时间将该时间的修改操作即可。

块长取 \(n^{\frac{2}{3}}\) 最优,复杂度是 \(O(n^{\frac{5}{3}})\)

Code
#include <bits/stdc++.h>
#define _for(i, a, b)  for (int i = (a); i <= (b); i ++ )
#define ll long long
#define PII pair<int, int>
#define fi first
#define se second
#define mp make_pair
using namespace std;
const int N = 1e6 + 5;
int n, q, B, qcnt, curans, a[N], bel[N], ans[N], cnt[N]; PII upd[N];
struct Query { int l, r, t, id; } qry[N];
inline void add(int x) { curans += ( ++ cnt[x]) == 1; }
inline void del(int x) { curans -= ! ( -- cnt[x]); }
signed main() {
	ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
	cin >> n >> q, B = pow(n, 0.67); char op; int l, r, t = 0;
	_for (i, 1, n)  cin >> a[i], bel[i] = (i - 1) / B + 1;
	_for (i, 1, q) {
		cin >> op >> l >> r;
		if (op == 'Q')  qry[ ++ qcnt] = (Query){l, r, i, qcnt};
		else  upd[i] = mp(l, r);
	} sort(qry + 1, qry + qcnt + 1, [&] (Query u, Query v) { return bel[u.l] ^ bel[v.l] ? bel[u.l] < bel[v.l] : (bel[u.r] ^ bel[v.r] ? bel[u.r] < bel[v.r] : (u.t < v.t)); }), l = 1, r = 0;
	_for (i, 1, qcnt) {
		while (l > qry[i].l)  add(a[ -- l]);
		while (r < qry[i].r)  add(a[ ++ r]);
		while (l < qry[i].l)  del(a[l ++ ]);
		while (r > qry[i].r)  del(a[r -- ]);
		while (t ^ qry[i].t) {
			if (t < qry[i].t)  t ++ ;
			if (l <= upd[t].fi && upd[t].fi <= r)  del(a[upd[t].fi]), add(upd[t].se); swap(a[upd[t].fi], upd[t].se);
			if (t > qry[i].t)  t -- ;
		} ans[qry[i].id] = curans;
	} _for (i, 1, qcnt)  cout << ans[i] << "\n";
	return 0;
}

CF940F Machine Learning

带修莫队。

用 bitset 维护出现次数即可,._Find_first() 查询第一个未出现的数。

Code
#include <bits/stdc++.h>
#define _for(i, a, b)  for (int i = (a); i <= (b); i ++ )
#define ll long long
#define PII pair<int, int>
#define fi first
#define se second
#define mp make_pair
using namespace std;
const int N = 1e5 + 5;
int n, q, B, x, qcnt, curans, b[N << 1], a[N], cnt[N << 1], bel[N], ans[N], cntcnt[N]; PII mdf[N]; bitset<N> f;
struct Query { int l, r, t, id; } qry[N];
inline void upd(int x, int k) {
	if (cnt[x] && cntcnt[cnt[x]] == 1)  f[cnt[x]] = 1; cntcnt[cnt[x]] -- , cntcnt[cnt[x] += k] ++ ;
	if (cnt[x] && cntcnt[cnt[x]] == 1)  f[cnt[x]] = 0;
} signed main() {
	ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
	cin >> n >> q, B = pow(n, 0.67), f.set(), f[0] = 0; int op, l, r, t = 0;
	_for (i, 1, n)  cin >> a[i], bel[i] = (i - 1) / B + 1, b[ ++ x] = a[i];
	_for (i, 1, q) {
		cin >> op >> l >> r;
		if (op == 1)  qry[ ++ qcnt] = (Query){l, r, i, qcnt};
		else  mdf[i] = mp(l, r), b[ ++ x] = r;
	} sort(qry + 1, qry + qcnt + 1, [&] (Query u, Query v) { return bel[u.l] ^ bel[v.l] ? bel[u.l] < bel[v.l] : (bel[u.r] ^ bel[v.r] ? bel[u.r] < bel[v.r] : (u.t < v.t)); }), l = 1, r = 0, sort(b + 1, b + x + 1), x = unique(b + 1, b + x + 1) - b - 1;
	_for (i, 1, n)  a[i] = lower_bound(b + 1, b + x + 1, a[i]) - b;
	_for (i, 1, q)  if (mdf[i].fi)  mdf[i].se = lower_bound(b + 1, b + x + 1, mdf[i].se) - b;
	_for (i, 1, qcnt) {
		while (l > qry[i].l)  upd(a[ -- l], 1);
		while (r < qry[i].r)  upd(a[ ++ r], 1);
		while (l < qry[i].l)  upd(a[l ++ ], - 1);
		while (r > qry[i].r)  upd(a[r -- ], - 1);
		while (t ^ qry[i].t) {
			if (t < qry[i].t)  t ++ ;
			if (l <= mdf[t].fi && mdf[t].fi <= r)  upd(a[mdf[t].fi], - 1), upd(mdf[t].se, 1); swap(a[mdf[t].fi], mdf[t].se);
			if (t > qry[i].t)  t -- ;
		} ans[qry[i].id] = f._Find_first();
	} _for (i, 1, qcnt)  cout << ans[i] << "\n";
	return 0;
}

SP10707 COT2 - Count on a tree II

树上莫队板子。

考虑欧拉序把一个点分成 \(in_i\)\(out_i\) 两个时间戳,一条链就是 \([in_x, in_y]\) 中只出现过一次的点的贡献。

复杂度是 \(O(n \sqrt n)\)

Code
#include <bits/stdc++.h>
#define _for(i, a, b)  for (int i = (a); i <= (b); i ++ )
#define pb push_back
using namespace std;
const int N = 4e5 + 5;
int n, q, B, t, dfn_cnt, timer, curans, a[N], b[N], p[N], ans[N], vis[N], cnt[N], Tin[N], Tout[N], bel[N], sz[N], son[N], dep[N], top[N], dfn[N], rnk[N], fa[N]; vector<int> Tr[N];
struct Query { int l, r, k, id; } qry[N];
void dfs1(int u) {
	sz[u] = 1, son[u] = - 1, p[Tin[u] = ++ timer] = u;
	for (int v : Tr[u])  if (v ^ fa[u]) {
		fa[v] = u, dep[v] = dep[u] + 1, dfs1(v), sz[u] += sz[v];
		if ( ! ~ son[u] || sz[son[u]] < sz[v])  son[u] = v;
	} p[Tout[u] = ++ timer] = u;
} void dfs2(int u, int Top) {
	rnk[dfn[u] = ++ dfn_cnt] = u, top[u] = Top;
	if ( ~ son[u])  dfs2(son[u], Top);
	for (int v : Tr[u])  if (v ^ son[u] && v ^ fa[u])  dfs2(v, v);
} inline int lca(int u, int v) {
	int U = top[u], V = top[v];
	while (U ^ V) {
		if (dep[U] >= dep[V])  u = fa[U];
		else  v = fa[V]; U = top[u], V = top[v];
	} return (dep[u] < dep[v] ? u : v);
} inline void upd(int x) { (vis[x] ? (curans -= ! ( -- cnt[a[x]])) : (curans += ! (cnt[a[x]] ++ ))), vis[x] ^= 1; }
signed main() {
	ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
	cin >> n >> q, B = sqrt(n); int u, v, w, l = 1, r = 0;
	_for (i, 1, n)  cin >> a[i], b[i] = a[i]; sort(b + 1, b + n + 1), t = unique(b + 1, b + n + 1) - b - 1;
	_for (i, 1, n << 1)  bel[i] = (i - 1) / B + 1;
	_for (i, 1, n)  a[i] = lower_bound(b + 1, b + t + 1, a[i]) - b;
	_for (i, 2, n)  cin >> u >> v, Tr[u].pb(v), Tr[v].pb(u); dfs1(1), dfs2(1, 1);
	_for (i, 1, q) {
		cin >> u >> v;
		if (Tin[u] > Tin[v])  swap(u, v);
		if ((w = lca(u, v)) == u)  qry[i] = (Query){Tin[u], Tin[v], 0, i};
		else  qry[i] = (Query){Tout[u], Tin[v], w, i};
	} sort(qry + 1, qry + q + 1, [&] (Query u, Query v) { return bel[u.l] ^ bel[v.l] ? bel[u.l] < bel[v.l] : (bel[u.l] & 1 ? u.r < v.r : u.r > v.r); });
	_for (i, 1, q) {
		while (l > qry[i].l)  upd(p[ -- l]);
		while (r < qry[i].r)  upd(p[ ++ r]);
		while (l < qry[i].l)  upd(p[l ++ ]);
		while (r > qry[i].r)  upd(p[r -- ]);
		if (qry[i].k)  curans += ! cnt[a[qry[i].k]]; ans[qry[i].id] = curans;
		if (qry[i].k)  curans -= ! cnt[a[qry[i].k]];
	} _for (i, 1, q)  cout << ans[i] << "\n";
	return 0;
}

P5268 [SNOI2017] 一个简单的询问

直接跑四维莫队会 TLE 两个点。

考虑拆贡献:设 \(f_{l_1, r_1, l_2, r_2}\)\(\sum\limits_{x=0}^\infty \text{get}(l_1,r_1,x)\times \text{get}(l_2,r_2,x)\),则有:

\[f_{l_1, r_1, l_2, r_2} = f_{1, r_1, 1, r_2} - f_{1, l_1 - 1, 1, r_2} - f_{1, r_1, 1, l_2 - 1} + f_{1, l_1 - 1, 1, l_2 - 1} \]

每个都是普通莫队。

Code
#include <bits/stdc++.h>
#define _for(i, a, b)  for (int i = (a); i <= (b); i ++ )
#define ll long long
using namespace std;
const int N = 4e5 + 5;
int n, m, q, B, a[N], bel[N], cnt1[N], cnt2[N]; ll curans, ans[N];
struct Query { int x, y, op, id; } qry[N];
signed main() {
	ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
	cin >> n, B = sqrt(n); int l1, r1, l2, r2, x = 0, y = 0;
	_for (i, 1, n)  cin >> a[i],  bel[i] = (i - 1) / B + 1; cin >> m;
	_for (i, 1, m)  cin >> l1 >> r1 >> l2 >> r2, qry[ ++ q] = (Query){r1, r2, 1, i}, qry[ ++ q] = (Query){l1 - 1, r2, - 1, i}, qry[ ++ q] = (Query){r1, l2 - 1, - 1, i}, qry[ ++ q] = (Query){l1 - 1, l2 - 1, 1, i}; sort(qry + 1, qry + q + 1, [&] (Query u, Query v) { return bel[u.x] ^ bel[v.x] ? bel[u.x] < bel[v.x] : (bel[u.x] & 1 ? u.y < v.y : u.y > v.y); });
	_for (i, 1, q) {
		while (x < qry[i].x)  x ++ , curans += cnt2[a[x]], cnt1[a[x]] ++ ;
		while (x > qry[i].x)  curans -= cnt2[a[x]], cnt1[a[x]] -- , x -- ;
		while (y < qry[i].y)  y ++ , curans += cnt1[a[y]], cnt2[a[y]] ++ ;
		while (y > qry[i].y)  curans -= cnt1[a[y]], cnt2[a[y]] -- , y -- ; ans[qry[i].id] += curans * qry[i].op;
	} _for (i, 1, m)  cout << ans[i] << "\n";
	return 0;
}

P4689 [Ynoi Easy Round 2016] 这是我自己的发明

和上一个题一样,只不过放到了树上,并且加了换根。

把树拍到 dfs 序上,然后就变成了区间问题,重点在于根在变。

画一下图就可以知道:

  • 如果 \(u\) 为当前根 \(rt\),则 \(u\) 的子树为所有点;

  • 如果 \(u\)\(rt\) 的祖先,找到 \(rt\) 的祖先 \(w\)\(w\)\(u\) 的儿子,则 \(u\) 的子树为 所有点去除掉 \(w\) 子树内的点(\(u\)\(rt\) 的祖先、\(rt\) 的祖先 \(w\)\(w\) 子树 均是在以 \(1\) 为根的条件下);

  • 否则,\(u\) 的子树就是以 \(1\) 为根时 \(u\) 的子树。

一个询问至多拆成 \(16\) 个莫队询问,然后跑莫队即可。

Code
#include <bits/stdc++.h>
#define _for(i, a, b)  for (int i = (a); i <= (b); i ++ )
#define _all(i, a, b)  for (int i = (a); i >= (b); i -- )
#define ll long long
#define pb push_back
using namespace std;
const int N = 5e5 + 5;
int n, q, m, B, t, k, rt = 1, L, dfn_cnt, timer, UP[N][21],a[N], b[N], p[N], vis[N], cnt1[N], cnt2[N], Tin[N], Tout[N], bel[N], sz[N], son[N], dep[N], top[N], dfn[N], rnk[N], fa[N]; ll curans, ans[N]; vector<int> Tr[N];
struct Query { int l, r, op, id; } qry[N << 4]; vector<Query> vecu, vecv;
void dfs1(int u) {
	sz[u] = 1, son[u] = - 1, Tin[u] = ++ timer, UP[u][0] = fa[u] ? fa[u] : u;
	_for (i, 1, L)  UP[u][i] = UP[UP[u][i - 1]][i - 1];
	for (int v : Tr[u])  if (v ^ fa[u]) {
		fa[v] = u, dep[v] = dep[u] + 1, dfs1(v), sz[u] += sz[v];
		if ( ! ~ son[u] || sz[son[u]] < sz[v])  son[u] = v;
	} Tout[u] = ++ timer;
} void dfs2(int u, int Top) {
	rnk[dfn[u] = ++ dfn_cnt] = u, top[u] = Top;
	if ( ~ son[u])  dfs2(son[u], Top);
	for (int v : Tr[u])  if (v ^ son[u] && v ^ fa[u])  dfs2(v, v);
} inline int lca(int u, int v) {
	int U = top[u], V = top[v];
	while (U ^ V) {
		if (dep[U] >= dep[V])  u = fa[U];
		else  v = fa[V]; U = top[u], V = top[v];
	} return (dep[u] < dep[v] ? u : v);
} inline bool is_ancestor(int u, int v) { return Tin[u] <= Tin[v] && Tout[v] <= Tout[u]; }
inline int climb(int u, int tar) { _all (i, L, 0)  if (is_ancestor(tar, UP[u][i]) && tar ^ UP[u][i])  u = UP[u][i]; return u; }
signed main() {
	ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
	cin >> n >> m, L = ceil(log2(n)), B = sqrt(n); int op, u, v, l = 0, r = 0, w;
	_for (i, 1, n)  cin >> a[i], b[i] = a[i], bel[i] = (i - 1) / B + 1; sort(b + 1, b + n + 1); t = unique(b + 1, b + n + 1) - b - 1;
	_for (i, 1, n)  a[i] = lower_bound(b + 1, b + t + 1, a[i]) - b;
	_for (i, 2, n)  cin >> u >> v, Tr[u].pb(v), Tr[v].pb(u); dfs1(1), dfs2(1, 1);
	_for (i, 1, m) {
		cin >> op >> u;
		if (op == 1)  rt = u;
		else {
			cin >> v, vecu.clear(), vecv.clear(), k ++ ;
			if (u == rt)  vecu.pb((Query){1, n, 1, 0});
			else if (is_ancestor(u, rt))  w = climb(rt, u), vecu.pb((Query){1, n, 1, 0}), vecu.pb((Query){dfn[w], dfn[w] + sz[w] - 1, - 1, 0});
			else  vecu.pb((Query){dfn[u], dfn[u] + sz[u] - 1, 1, 0});
			if (v == rt)  vecv.pb((Query){1, n, 1, 0});
			else if (is_ancestor(v, rt))  w = climb(rt, v), vecv.pb((Query){1, n, 1, 0}), vecv.pb((Query){dfn[w], dfn[w] + sz[w] - 1, - 1, 0});
			else  vecv.pb((Query){dfn[v], dfn[v] + sz[v] - 1, 1, 0});
			for (Query x : vecu)  for (Query y : vecv)  qry[ ++ q] = (Query){x.r, y.r, x.op * y.op, k}, qry[ ++ q] = (Query){x.l - 1, y.r, - x.op * y.op, k}, qry[ ++ q] = (Query){x.r, y.l - 1, - x.op * y.op, k}, qry[ ++ q] = (Query){x.l - 1, y.l - 1, x.op * y.op, k};
		}
	} sort(qry + 1, qry + q + 1, [&] (Query u, Query v) { return bel[u.l] ^ bel[v.l] ? bel[u.l] < bel[v.l] : (bel[u.l] & 1 ? u.r < v.r : u.r > v.r); });
	_for (i, 1, q) {
		while (l < qry[i].l)  l ++ , curans += cnt2[a[rnk[l]]], cnt1[a[rnk[l]]] ++ ;
		while (l > qry[i].l)  curans -= cnt2[a[rnk[l]]], cnt1[a[rnk[l]]] -- , l -- ;
		while (r < qry[i].r)  r ++ , curans += cnt1[a[rnk[r]]], cnt2[a[rnk[r]]] ++ ;
		while (r > qry[i].r)  curans -= cnt1[a[rnk[r]]], cnt2[a[rnk[r]]] -- , r -- ; ans[qry[i].id] += curans * qry[i].op;
	} _for (i, 1, k)  cout << ans[i] << "\n";
	return 0;
}

P5398 [Ynoi2018] GOSICK

很不错的题,只不过写起来有点 fenter。

考虑莫队,加入 / 删除 \(x\) 带来的贡献分为两种:\(x\) 的倍数的个数 和 \(x\) 的因数的个数。

发现复杂度很高,考虑二次离线下来。现在考虑有 \(O(n)\) 次加入值,\(O(n \sqrt n)\) 次查询。我们必须做到单次查询 \(O(1)\)

  • \(x\) 的倍数:每加入一个数花费 \(O(\sqrt V)\) 的时间枚举因数 \(i\),将 \(i\) 对应的桶 \(c_i \leftarrow c_i + 1\),表示多了一个 \(i\) 的倍数;

  • \(x\) 的因数:加入一个数 \(a\) 时,枚举其倍数进行修改即可做到 \(O(1)\) 查询,容易想到阈值分治(设阈值为 \(B\)):

    • \(a > B\)\(a\) 的倍数 \(\le \frac{V}{B}\) 个,暴力枚举即可。

    • \(a \le B\):难点。考虑到只有 \(B\) 个此类值,容易想到对每个 \(a\) 单独做,最后再 \([1, B]\) 的答案加起来即可。对于一个 \(a\),维护一个长度为 \(n\)\(0/1\) 序列,表示第 \(i\) 个位置是不是 \(a\) 的倍数。先做一遍前缀和,那么查询就相当于是差分了。这一部分复杂度是 \(O(B(n + q))\) 的。

然后做完了。视 \(n, q, V\) 同阶,复杂度 \(O(n \sqrt n)\)

讲一下卡常:

  • 莫队二离的时候不要用 vector 存储对应位置的询问。开数组 sort,然后用指针状物扫,常数会小很多

  • 对于所有序列中出现过的数字,用 vector 预先存下其因数,后面用的话不用再 \(O(\sqrt V)\) 枚举了,降为 \(O(\log V)\)(千万不要像我一开始把 \(\le 5 \times 10^5\) 的所有数的因数预处理了,很多信息是完全没必要的)。

  • 莫队一些能 \(O(1)\) 计算区间信息的就 \(O(1)\) 算,不要每次都一个一个地移动 \(l, r\) 指针。这会使你程序的某些部分从 \(O(n \sqrt n)\) 变为 \(O(n)\)

  • 由于显而易见的原因,本题的阈值 \(B\) 要尽量往小开

  • 重复的枚举或计算可以一起处理。

Code
#include <bits/stdc++.h>
#define _for(i, a, b)  for (int i = (a); i <= (b); i ++ )
#define ll long long
using namespace std;
namespace IO {
	const int LIM = 1e6;
	static char buf[LIM], * p1 = buf, * p2 = buf, obuf[LIM], * p3 = obuf, cc[20];
	#define gc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, LIM, stdin), p1 == p2) ? EOF : * p1 ++ )
	#define pc(x) (p3 - obuf < LIM) ? ( * p3 ++ = x) : (fwrite(obuf, p3 - obuf, 1, stdout), p3 = obuf, * p3 ++ = x)
	inline int rd() {
		int val = 0, sgn = 1; char ch = gc();
		for ( ; ch < '0' || ch > '9'; ch = gc())  if (ch == '-')  sgn = - 1;
		for ( ; ch >= '0' && ch <= '9'; ch = gc())  val = (val << 3) + (val << 1) + (ch ^ 48); return (val * sgn);
	} inline void wt(ll x, char c) {
		if ( ! x)  pc('0'); int len = 0;
		if (x < 0)  x = - x, pc('-');
		while (x)  cc[len ++ ] = x % 10 | '0', x /= 10;
		while (len -- )  pc(cc[len]); pc(c);
	}
} using namespace IO;
const int N = 5e5 + 5, V = 25;
int n, q, B, m, bel[N], a[N], hd[N], L[N], R[N], c1[N], c2[N], p[N]; ll cur_ans, cur[N], ans[N]; vector<int> fact[N]; ll f[N], res[N << 1]; bitset<N> occ;
struct Node { int l, r, id, op; } qry[N], vec[N << 1];
signed main() {
	ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
	n = rd(), q = rd(), B = sqrt(q); int l = 1, r = 0, cur = 1, t;
	_for (i, 1, n) {
		a[i] = rd(), bel[i] = (i - 1) / B + 1;
		if ( ! occ[a[i]])  _for (j, 1, sqrt(a[i]))  if ( ! (a[i] % j)) {
			fact[a[i]].emplace_back(j);
			if ((j * j) ^ a[i])  fact[a[i]].emplace_back(a[i] / j);
		} occ[a[i]] = 1;
	} _for (i, 1, q)  qry[i].l = rd(), qry[i].r = rd(), qry[i].id = i, qry[i].op = 0; sort(qry + 1, qry + q + 1, [&] (Node u, Node v) { return bel[u.l] ^ bel[v.l] ? bel[u.l] < bel[v.l] : (bel[u.l] & 1 ? u.r < v.r : u.r > v.r); });
	_for (i, 1, n) {
		f[i] = f[i - 1], c1[a[i]] ++ ;
		for (int j : fact[a[i]])  c2[j] ++ , f[i] += c1[j]; f[i] += c2[a[i]]; f[i] -- ;
	} _for (i, 1, q) {
		if (l > qry[i].l)  vec[ ++ m] = (Node){qry[i].l, l - 1, r, m}, cur_ans -= f[l - 1] - f[qry[i].l - 1], l = qry[i].l;
		if (r < qry[i].r)  vec[ ++ m] = (Node){r + 1, qry[i].r, l - 1, m}, cur_ans += f[qry[i].r] - f[r] - (qry[i].r - r), r = qry[i].r;
		if (l < qry[i].l)  vec[ ++ m] = (Node){l, qry[i].l - 1, r, m}, cur_ans += f[qry[i].l - 1] - f[l - 1], l = qry[i].l;
		if (r > qry[i].r)  vec[ ++ m] = (Node){qry[i].r + 1, r, l - 1, m}, cur_ans -= f[r] - f[qry[i].r] - (r - qry[i].r), r = qry[i].r; ans[qry[i].id] += cur_ans + r - l + 1;
	} _for (i, 0, N - 1)  c1[i] = c2[i] = 0;
	sort(vec + 1, vec + m + 1, [&] (Node u, Node v) { return u.id < v.id; });
	_for (i, 1, n) {
		for (int j : fact[a[i]])  c2[j] ++ ;
		if (a[i] > V)  _for (j, 1, (N - 1) / a[i])  c1[a[i] * j] ++ ;
		while (cur <= m && vec[cur].id < i)  cur ++ ;
		while (cur <= m && vec[cur].id == i) {
			if ( ! L[i])  L[i] = cur; R[i] = cur;
			_for (k, vec[cur].l, vec[cur].r)  res[vec[cur].op] += c1[a[k]] + c2[a[k]] - (k <= i); cur ++ ;
		}
	}  _for (w, 1, V)  if (occ[w]) {
		t = 0;
		_for (i, 1, n) {
			p[i] = p[i - 1];
			if ( ! (a[i] % w))  p[i] ++ ;
		} _for (i, 1, n) {
			t += a[i] == w;
			_for (cur, L[i], R[i])  res[vec[cur].op] += 1ll * t * (p[vec[cur].r] - p[vec[cur].l - 1]);
		}
	} l = 1, r = cur_ans = cur = 0;
	_for (i, 1, q) {
		if (l > qry[i].l)  cur_ans += res[ ++ cur], l = qry[i].l;
		if (r < qry[i].r)  cur_ans -= res[ ++ cur], r = qry[i].r;
		if (l < qry[i].l)  cur_ans -= res[ ++ cur], l = qry[i].l;
		if (r > qry[i].r)  cur_ans += res[ ++ cur], r = qry[i].r; ans[qry[i].id] += cur_ans;
	} _for (i, 1, q)  wt(ans[i], '\n');
	return fwrite(obuf, p3 - obuf, 1, stdout), 0;
}

P6578 [Ynoi2019] 魔法少女网站

考虑转化为:\(a_i \le x\) 的位置为 \(1\),否则为 \(0\),求 \([l, r]\) 之内的全为 \(1\) 的子段个数。

如果不带修,是典型的将询问按照 \(x\) 排序,从小到大加入每个数,然后分块维护长度为 \(1\) 的段。具体可见 P5386 [Cnoi2019] 数字游戏

带修,考虑操作分块:一个块内的修改位置先不管,把为修改过的位置做上述操作,然后将块内的询问排序操作,每个询问要将修改的位置贡献算进去。处理完一个块后重构。

一共 \(q\) 个询问,对于每个询问要枚举 \(\sqrt q\) 个修改操作,花 \(O(\sqrt n)\) 的时间计算答案。同时每个操作块也要花 \(O(n)\) 的时间重构。

\(n, q\) 同阶,复杂度 \(O(n \sqrt n)\)。卡常了很久才过 /tuu。

Code
#include <bits/stdc++.h>
#define _for(i, a, b)  for (register int i = (a); i <= (b); i ++ )
#define _all(i, a, b)  for (register int i = (a); i >= (b); i -- )
#define ll long long
#define PII pair<int, int>
#define PIL pair<int, ll>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
using namespace std;
namespace IO {
	const int LIM = 1 << 20;
	static char buf[LIM], * p1 = buf, * p2 = buf, obuf[LIM], * p3 = obuf, cc[20];
	#define gc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, LIM, stdin), p1 == p2) ? EOF : * p1 ++ )
	#define pc(x) (p3 - obuf < LIM) ? ( * p3 ++ = x) : (fwrite(obuf, p3 - obuf, 1, stdout), p3 = obuf, * p3 ++ = x)
	inline int rd() {
		int val = 0; char ch = gc();
		for ( ; ch < '0' || ch > '9'; ch = gc()) ;
		for ( ; ch >= '0' && ch <= '9'; ch = gc())  val = (val << 3) + (val << 1) + (ch ^ 48); return val;
	} inline void wt(ll x, char c) {
		if ( ! x)  pc('0'); int len = 0;
		while (x)  cc[len ++ ] = x % 10 | '0', x /= 10;
		while (len -- )  pc(cc[len]); pc(c);
	}
} using namespace IO;
const int N = 3e5 + 5;
int n, q, m, mq, ztysb, B, Bq, Fuck, aaa, lpcnt, rpcnt, rescnt, e, hd[N], to[N], nxt[N], cnt[N], ff[N], LL[N], RR[N], f[N], g[N], aa[N], fuck[N], a[N], bel[N], L[N], R[N], pp[N], Lq[N], tt[N], Rq[N], belq[N], pos[N], lp[N], rp[N]; ll work[N], res[N]; PII LP[N], RP[N]; PIL RES[N]; vector<int> vec[N]; ll ans[N];
struct Node { int op, l, r, x; } b[N]; bitset<N> val, vis;
inline void add(int op, int x) {
	int bl = bel[x], pl = bel[x - 1] == bl ? x - lp[x - 1] : x, pr = bel[x + 1] == bl ? x + rp[x + 1] : x; val[x] = 1;
	if ( ! op)  res[bl] += 1ll * (pr - x + 1) * (x - pl + 1), rp[pl] = lp[pr] = pr - pl + 1;
	else  RES[ ++ rescnt] = mp(bl, res[bl]), res[bl] += 1ll * (pr - x + 1) * (x - pl + 1), RP[ ++ rpcnt] = mp(pl, rp[pl]), LP[ ++ lpcnt] = mp(pr, lp[pr]), rp[pl] = lp[pr] = pr - pl + 1;
} inline ll calc_bf(int l, int r) {
	int len = 0, flag = 0; ll ans = 0;
	_for (i, l, r) {
		if (val[i]) {
			if ( ! flag)  len ++ ;
			else  ans += work[min(r, i + rp[i] - 1) - i + 1], i = i + rp[i] - 1;
		} else  ans += work[len], len = 0, flag = 1, i = val._Find_next(i) - 1;
		if (i < l || i > r)  break;
	} return ans + work[len];
} inline ll calc(int l, int r) {
	int bl = bel[l], br = bel[r]; ll ans = 0;
	if (bl == br)  ans = calc_bf(l, r);
	else {
		ans = calc_bf(l, R[bl]) + calc_bf(L[br], r) - work[min(R[bl] - l + 1, lp[R[bl]])] - work[min(rp[L[br]], r - L[br] + 1)]; int len = min(R[bl] - l + 1, lp[R[bl]]);
		_for (i, bl + 1, br - 1) {
			if (rp[L[i]] == R[i] - L[i] + 1)  len += R[i] - L[i] + 1;
			else  ans += work[len + rp[L[i]]] + res[i] - work[rp[L[i]]] - work[lp[R[i]]], len = lp[R[i]];
		} ans += work[len + min(rp[L[br]], r - L[br] + 1)];
	} _all (i, lpcnt, 1)  lp[LP[i].fi] = LP[i].se;
	_all (i, rpcnt, 1)  rp[RP[i].fi] = RP[i].se;
	_all (i, rescnt, 1)  res[RES[i].fi] = RES[i].se; lpcnt = rpcnt = rescnt = 0; return ans;
} signed main() {
	n = rd(), q = rd(), B = max(1, (int)(sqrt(n) / 1.25)), Bq = max(1700, (int)(sqrt(q) * 2.5)); int cur, zty;
	_for (i, 1, n)  a[i] = rd(), bel[i] = (i - 1) / B + 1;
	_for (i, 1, N - 1)  work[i] = work[i - 1] + i; m = bel[n];
	_for (i, 1, q)  belq[i] = (i - 1) / Bq + 1; mq = belq[q];
	_for (i, 1, m)  L[i] = (i - 1) * B + 1, R[i] = min(n, i * B);
	_for (i, 1, mq)  Lq[i] = (i - 1) * Bq + 1, Rq[i] = min(q, i * Bq);
	_for (tt, 1, q) {
		b[tt].op = rd(), b[tt].l = rd(), b[tt].r = rd(); if (b[tt].op == 2)  b[tt].x = rd();
		if (tt == Rq[belq[tt]]) {
			int i = belq[tt]; ztysb = cur = aaa = e = 0;
			_for (j, Lq[i], Rq[i]) {
				if (b[j].op == 1 && ! vis[b[j].l])  vis[b[j].l] = 1, aa[ ++ aaa] = b[j].l;
				else if (b[j].op == 2)  f[ ++ ztysb] = j;
			} sort(f + 1, f + ztysb + 1, [&] (int x, int y) { return b[x].x < b[y].x; }), fill(cnt + 1, cnt + n + 1, 0);
			_for (j, 1, n)  if ( ! vis[j])  to[ ++ e] = j, nxt[e] = hd[a[j]], hd[a[j]] = e; zty = Lq[i];
			_for (j, 1, ztysb) {
				_for (k, b[f[j - 1]].x + 1, b[f[j]].x)  for (int kk = hd[k]; kk; kk = nxt[kk])  add(0, to[kk]); Fuck = 0;
				_for (k, zty, f[j] - 1)  if (b[k].op == 1)  swap(a[b[k].l], b[k].r);
				_for (k, 1, aaa)  if (a[aa[k]] <= b[f[j]].x)  add(1, aa[k]), fuck[ ++ Fuck] = aa[k]; ans[f[j]] = calc(b[f[j]].l, b[f[j]].r);
				if (f[j] > f[j + 1])  _all (k, f[j] - 1, Lq[i])  if (b[k].op == 1)  swap(a[b[k].l], b[k].r);
				_for (k, 1, Fuck)  val[fuck[k]] = 0; zty = j < ztysb && f[j] <= f[j + 1] ? f[j] + 1 : Lq[i];
			} _for (j, 0, n + 1)  lp[j] = rp[j] = res[j] = hd[j] = 0; lpcnt = rpcnt = rescnt = 0, val.reset(), vis.reset();
			_for (j, Lq[i], Rq[i])  if (b[j].op == 1)  a[b[j].l] = b[j].r;
		}
	} _for (i, 1, q)  if (b[i].op == 2)  wt(ans[i], '\n');
	return fwrite(obuf, p3 - obuf, 1, stdout), 0;
}

U210802 人类智慧分块

二维分块。/bx hjx 的博客

Code
#include <bits/stdc++.h>
#define _for(i, a, b)  for (int i = (a); i <= (b); i ++ )
using namespace std;
const int N = 4e5 + 5, P = 20, V = 400;
int n, q, p, cnt, xx[N], yy[N], id1[P][P], id2[V][V], id3[P][V], id4[V][P], val[N], A[N], B[N];
inline int bel(int x, int B) { return (x - 1) / B + 1; }
inline void update(int x, int y) {
	xx[x] = y, yy[y] = x;
	val[id1[bel(x, p * p * p)][bel(y, p * p * p)]] ++ ;
	val[id2[bel(x, p * p)][bel(y, p * p)]] ++ ;
	val[id3[bel(x, p * p * p)][bel(y, p * p)]] ++ ;
	val[id4[bel(x, p * p)][bel(y, p * p * p)]] ++ ;
} inline int query(int x, int y) {
	int res = 0, tx = bel(x, p * p * p) - 1, ty = bel(y, p * p * p) - 1, ex = bel(x, p * p) - 1, ey = bel(y, p * p) - 1;
	_for (i, 1, tx)  _for (j, 1, ty)  res += val[id1[i][j]];
	_for (i, tx * p + 1, ex)  _for (j, ty * p + 1, ey)  res += val[id2[i][j]];
	_for (i, 1, tx)  _for (j, ty * p + 1, ey)  res += val[id3[i][j]];
	_for (i, tx * p + 1, ex)  _for (j, 1, ty)  res += val[id4[i][j]];
	_for (i, ex * p * p + 1, x)  res += 1 <= xx[i] && xx[i] <= y;
	_for (i, ey * p * p + 1, y)  res += 1 <= yy[i] && yy[i] <= ex * p * p; return res;
} signed main() {
	ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
	cin >> n >> q; int op, x, y;
	_for (i, 1, n)  cin >> A[i] >> B[i]; p = ceil(pow(n, 0.25)), n = p * p * p * p;
	_for (i, 1, p)  _for (j, 1, p)  id1[i][j] = ++ cnt;
	_for (i, 1, p * p)  _for (j, 1, p * p)  id2[i][j] = ++ cnt;
	_for (i, 1, p)  _for (j, 1, p * p)  id3[i][j] = ++ cnt;
	_for (i, 1, p * p)  _for (j, 1, p)  id4[i][j] = ++ cnt; cnt = 0;
	while (q -- ) {
		cin >> op >> x;
		if (op == 1)  cin >> y, update(x, y);
		else  cout << query(A[x], B[x]) << "\n";
	}
	return 0;
}

P7601 [THUPC 2021] 区间本质不同逆序对 / P7448 [Ynoi2007] rdiq

强于 P5047 [Ynoi2019 模拟赛] Yuno loves sqrt technology II,同样考虑莫队二离。

假设从 \([l, r - 1]\) 转移到 \([l, r]\)。为了满足相同的数只算一次,我们需要满足 \(a_i > a_r\)\(pre_i < l\)\(i > pre_r\)

\(O(n)\) 次修改,\(O(n \sqrt n)\) 次查询。用 \(O(\sqrt n)\) 查询,\(O(1)\) 查询的二维分块平衡即可。

为了让点尽量分散,需要将序列离散化,相同的数强制赋值成不同的数(越靠前越小)。

Code
#include <bits/stdc++.h>
#define _for(i, a, b)  for (int i = (a); i <= (b); i ++ )
#define _all(i, a, b)  for (int i = (a); i >= (b); i -- )
#define ll long long
#define pb push_back
using namespace std;
const int N = 1e6 + 5, V = 30;
int n, q, B, a[N], id[N], bel[N], pre[N], nxt[N], lst[N], val[N], cnt[N]; ll ans[N];
struct Node { int l, r, id, op; }; vector<Node> vec1[N], vec2[N];
struct Query { int l, r, id; } qry[N];
struct Two_Dimensional_Blocking {
	int p, nn, cnt, A[N], B[N], bel[N][2], id1[V][V], id2[V * V][V * V], id3[V][V * V], id4[V * V][V], val[N], rval[N]; vector<int> xx[N], yy[N];
	inline void build() {
		p = ceil(pow(nn = n + 1, 0.25)), nn = p * p * p * p, cnt = 0;
		_for (i, 1, n + 1)  bel[i][0] = (i - 1) / (p * p * p) + 1, bel[i][1] = (i - 1) / (p * p) + 1;
		_for (i, 1, p)  _for (j, 1, p)  id1[i][j] = ++ cnt;
		_for (i, 1, p * p)  _for (j, 1, p * p)  id2[i][j] = ++ cnt;
		_for (i, 1, p)  _for (j, 1, p * p)  id3[i][j] = ++ cnt;
		_for (i, 1, p * p)  _for (j, 1, p)  id4[i][j] = ++ cnt;
	} inline void init(int * AA, int * BB) {
		_for (i, 1, p * p)  xx[i].clear(), yy[i].clear(); memset(val, 0, sizeof(val)), memset(rval, 0, sizeof(rval));
		_for (i, 1, n)  A[i] = AA[i], B[i] = BB[i], xx[bel[A[i]][1]].pb(i), yy[bel[B[i]][1]].pb(i);
	} inline void update(int x, int y, int k) {
		int ex = bel[x][0], ey = bel[y][0], tx = bel[x][1], ty = bel[y][1];
		_for (i, ex + 1, p)  _for (j, ey + 1, p)  val[id1[i][j]] += k;
		_for (i, tx + 1, ex * p)  _for (j, ty + 1, ey * p)  val[id2[i][j]] += k;
		_for (i, ex + 1, p)  _for (j, ty + 1, ey * p)  val[id3[i][j]] += k;
		_for (i, tx + 1, ex * p)  _for (j, ey + 1, p)  val[id4[i][j]] += k;
		for (int i : xx[tx])  if (A[i] >= x && B[i] >= y)  rval[i] += k;
		for (int i : yy[ty])  if (A[i] >= x && B[i] >= y && tx ^ bel[A[i]][1])  rval[i] += k;
	} inline int query(int id) { return id ? rval[id] + val[id1[bel[A[id]][0]][bel[B[id]][0]]] + val[id2[bel[A[id]][1]][bel[B[id]][1]]] + val[id3[bel[A[id]][0]][bel[B[id]][1]]] + val[id4[bel[A[id]][1]][bel[B[id]][0]]] : 0; }
} Block;
signed main() {
	ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
	cin >> n, B = sqrt(n), Block.build(); int l = 1, r = 0;
	_for (i, 1, n)  cin >> a[i], pre[i] = lst[a[i]], lst[a[i]] = i, cnt[a[i]] ++ , bel[i] = (i - 1) / B + 1; fill(lst + 1, lst + n + 1, 0);
	_all (i, n, 1)  nxt[i] = lst[a[i]], lst[a[i]] = i;
	_for (i, 1, n)  cnt[i] += cnt[i - 1];
	_all (i, n, 1)  val[i] = cnt[a[i]] -- ; cin >> q;
	_for (i, 1, q)  cin >> qry[i].l >> qry[i].r, qry[i].id = i; sort(qry + 1, qry + q + 1, [&] (Query u, Query v) { return bel[u.l] ^ bel[v.l] ? bel[u.l] < bel[v.l] : (bel[u.l] & 1 ? u.r < v.r : u.r > v.r); });
	_for (i, 1, q) {
		if (l > qry[i].l)  vec2[r].pb((Node){qry[i].l, l - 1, qry[i].id, 1}), l = qry[i].l;
		if (r < qry[i].r)  vec1[l].pb((Node){r + 1, qry[i].r, qry[i].id, 1}), r = qry[i].r;
		if (l < qry[i].l)  vec2[r].pb((Node){l, qry[i].l - 1, qry[i].id, - 1}), l = qry[i].l;
		if (r > qry[i].r)  vec1[l].pb((Node){qry[i].r + 1, r, qry[i].id, - 1}), r = qry[i].r;
	} _for (i, 1, n)  id[i] = i, val[i] = n - val[i] + 1; Block.init(id, val);
	_all (i, n, 1) {
		if (nxt[i])  Block.update(id[nxt[i]], val[nxt[i]] + 1, - 1); Block.update(id[i], val[i] + 1, 1);
		for (Node j : vec1[i])  _for (k, j.l, j.r)  ans[j.id] += (Block.query(k) - Block.query(pre[k])) * j.op;
	} _for (i, 1, n)  id[i] = n - i + 1, val[i] = n - val[i] + 1; Block.init(id, val);
	_for (i, 1, n) {
		if (pre[i])  Block.update(id[pre[i]], val[pre[i]] + 1, - 1); Block.update(id[i], val[i] + 1, 1);
		for (Node j : vec2[i])  _for (k, j.l, j.r)  ans[j.id] += (Block.query(k) - Block.query(nxt[k])) * j.op;
	} _for (i, 1, q)  ans[qry[i].id] += ans[qry[i - 1].id];
	_for (i, 1, q)  cout << ans[i] << "\n";
	return 0;
}

P8530 [Ynoi2003] 博丽灵梦

二维带权数颜色。

考虑一维的做法:我们维护 \(pre_i\) 表示在 \(i\) 之前的最大的满足 \(a_i = a_j\)\(j\)(不存在则为 \(0\))。为了不算重,对于一个区间 \([l, r]\) 和颜色 \(c\),我们在颜色 \(c\) 第一次出现的位置 \(p\) 处计算贡献。也就是说 \(\sum [l \le p \le r] \cap [pre_p < l]\)。这是一个二维数点问题。

如果二维直接套用上述一维的做法较为困难,因为你找不出一个合适的序来刻画点和 \(pre\) 数组。于是考虑莫队毙掉一维。现在有 \(O(n \sqrt n)\) 次修改和 \(O(q)\) 次查询。我们需要一个 \(O(1)\) 修改 / \(O(\sqrt n)\) 查询的数据结构维护单点加,矩形查和,二维分块即可。

最后如何快速求出 \(pre\) 呢?我们发现动态删除一个点是好做的(类似链表直接把前驱和后继连接即可),但加入一个点需要带 \(\log\)。于是考虑只有删除 + 撤销的莫队,类似回滚莫队,先把整个序列加进去,然后倒着删除。这样就不带 \(\log\) 了。

最终复杂度 \(O((n + q) \sqrt n)\)。不卡常,调一下块长就过了。

Code
#include <bits/stdc++.h>
#define _for(i, a, b)  for (int i = (a); i <= (b); i ++ )
#define _all(i, a, b)  for (int i = (a); i >= (b); i -- )
#define uint unsigned int
using namespace std;
namespace IO {
	const int LIM = 1e5;
	static char buf[LIM], * p1 = buf, * p2 = buf, obuf[LIM], * p3 = obuf, cc[20];
	#define gc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, LIM, stdin), p1 == p2) ? EOF : * p1 ++ )
	#define pc(x) (p3 - obuf < LIM) ? ( * p3 ++ = x) : (fwrite(obuf, p3 - obuf, 1, stdout), p3 = obuf, * p3 ++ = x)
	inline int rd() {
		int val = 0, sgn = 1; char ch = gc();
		for ( ; ch < '0' || ch > '9'; ch = gc())  if (ch == '-')  sgn = - 1;
		for ( ; ch >= '0' && ch <= '9'; ch = gc())  val = (val << 3) + (val << 1) + (ch ^ 48); return (val * sgn);
	} inline void wt(uint x, char c) {
		if ( ! x)  pc('0'); int len = 0;
		while (x)  cc[len ++ ] = x % 10 | '0', x /= 10;
		while (len -- )  pc(cc[len]); pc(c);
	}
} using namespace IO;
const int N = 1.3e5 + 4, Q = 1e6 + 5, P = 20, V = 400;
int n, q, B, top, top1, top2, cnt[N], L[N], R[N], bel[N], pre[N], nxt[N], a[N], w[N], p[N], pos[N], lst[N]; uint ans[Q]; struct Node { int l, r, l2, r2, id; } qry[Q]; struct Rollback { int x, y, val; } rb[N], rb1[N], rb2[N];
struct Blocking {
	int cnt = 0, p, xx[N], yy[N], bel1[N], bel2[N], id1[P][P], id2[V][V], id3[P][V], id4[V][P]; uint val[N], tag0[N], val0[N];
	inline void init() {
		p = ceil(pow(n + 5, 0.25));
		_for (i, 1, p * p * p * p)  bel1[i] = (i - 1) / (p * p * p) + 1, bel2[i] = (i - 1) / (p * p) + 1;
		_for (i, 1, p)  _for (j, 1, p)  id1[i][j] = ++ cnt;
		_for (i, 1, p * p)  _for (j, 1, p * p)  id2[i][j] = ++ cnt;
		_for (i, 1, p)  _for (j, 1, p * p)  id3[i][j] = ++ cnt;
		_for (i, 1, p * p)  _for (j, 1, p)  id4[i][j] = ++ cnt;
	} inline void clear() {
		_for (i, 1, cnt)  val[i] = 0;
		_for (i, 0, n)  tag0[i] = val0[i] = xx[i] = yy[i] = 0;
	} inline void update(int x, int y, int k) {
		if (y) {
			if (k > 0)  xx[x] = y, yy[y] = x;
			else  xx[x] = yy[y] = 0; val[id1[bel1[x]][bel1[y]]] += k, val[id2[bel2[x]][bel2[y]]] += k, val[id3[bel1[x]][bel2[y]]] += k, val[id4[bel2[x]][bel1[y]]] += k;
		} else  tag0[bel2[x]] += k, val0[x] += k;
	} inline int query(int x, int y) {
		int tx = bel1[x] - 1, ty = bel1[y] - 1, ex = bel2[x] - 1, ey = bel2[y] - 1; uint res = 0;
		_for (i, 1, tx)  _for (j, 1, ty)  res += val[id1[i][j]];
		_for (i, tx * p + 1, ex)  _for (j, ty * p + 1, ey)  res += val[id2[i][j]];
		_for (i, 1, tx)  _for (j, ty * p + 1, ey)  res += val[id3[i][j]];
		_for (i, tx * p + 1, ex)  _for (j, 1, ty)  res += val[id4[i][j]];
		_for (i, ex * p * p + 1, x)  res += (1 <= xx[i] && xx[i] <= y) * w[a[pos[i]]];
		_for (i, ey * p * p + 1, y)  res += (1 <= yy[i] && yy[i] <= ex * p * p) * w[a[pos[i]]];
		_for (i, 1, ex)  res += tag0[i];
		_for (i, ex * p * p + 1, x)  res += val0[i]; return res;
	}
} Block;
inline void del(int x, int op) {
	int p = pre[x], q = nxt[x];
	if ( ! op) {
		Block.update(x, pre[x], - w[a[pos[x]]]);
		if (q)  Block.update(q, pre[q], - w[a[pos[q]]]), Block.update(q, pre[q] = p, w[a[pos[q]]]); nxt[p] = q;
	} else {
		rb[ ++ top] = (Rollback){x, pre[x], - w[a[pos[x]]]}, Block.update(x, pre[x], - w[a[pos[x]]]);
		if (q)  rb[ ++ top] = (Rollback){q, pre[q], - w[a[pos[q]]]}, Block.update(q, pre[q], - w[a[pos[q]]]), rb1[ ++ top1] = (Rollback){q, 0, pre[q]}, rb[ ++ top] = (Rollback){q, pre[q] = p, w[a[pos[q]]]}, Block.update(q, pre[q], w[a[pos[q]]]); rb2[ ++ top2] = (Rollback){p, 0, nxt[p]}, nxt[p] = q;
	}
} inline void rollback() {
	while (top)  Block.update(rb[top].x, rb[top].y, - rb[top].val), top -- ;
	while (top1)  pre[rb1[top1].x] = rb1[top1].val, top1 -- ;
	while (top2)  nxt[rb2[top2].x] = rb2[top2].val, top2 -- ;
} signed main() {
	ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
	B = max(1, (int)sqrt(n = rd()) / 2); int cur, l, r, l2, r2, now = 1; Block.init();
	_for (i, 1, n)  p[i] = rd();
	_for (i, 1, n)  a[i] = rd(), bel[i] = (i - 1) / B + 1;
	_for (i, 1, n)  w[i] = rd(); q = rd();
	_for (i, 1, bel[n])  L[i] = (i - 1) * B + 1, R[i] = min(n, i * B);
	_for (i, 1, q)  l = rd(), r = rd(), l2 = rd(), r2 = rd(), qry[i] = (Node){l, r, l2, r2, i}, cnt[bel[l]] ++ ; sort(qry + 1, qry + q + 1, [&] (Node x, Node y) { return bel[x.l] ^ bel[y.l] ? bel[x.l] < bel[y.l] : x.r > y.r; });
	_for (b, 1, bel[n])  if (cnt[b]) {
		Block.clear();
		_for (i, 0, n)  pos[i] = pre[i] = nxt[i] = 0; top = top1 = top2 = 0;
		_for (i, L[b], cur = qry[now].r)  pos[p[i]] = i;
		_for (i, 0, n)  lst[i] = 0;
		_for (i, 1, n)  if (pos[i])  pre[i] = lst[a[pos[i]]], lst[a[pos[i]]] = i;
		_for (i, 0, n)  lst[i] = 0;
		_all (i, n, 1)  if (pos[i])  nxt[i] = lst[a[pos[i]]], lst[a[pos[i]]] = i;
		_for (i, 1, n)  if (pos[i])  Block.update(i, pre[i], w[a[pos[i]]]);
		while (bel[qry[now].l] == b) {
			while (cur > qry[now].r)  del(p[cur -- ], 0);
			_for (j, L[b], qry[now].l - 1)  del(p[j], 1); ans[qry[now].id] = Block.query(qry[now].r2, qry[now].l2 - 1) - Block.query(qry[now].l2 - 1, qry[now].l2 - 1), rollback(); now ++ ;
		}
	} _for (i, 1, q)  wt(ans[i], '\n');
	return fwrite(obuf, p3 - obuf, 1, stdout), 0;
}

根号分治 & 根号相关

CF700D Huffman Coding on Segment

首先哈夫曼编码做一轮是 \(O(n)\) 的,\(n\) 是数的个数。

考虑上莫队,然后根号分治,设阈值为 \(B\)

我们想让每次暴力跑哈夫曼的时候元素个数 \(\le B\)。于是让每种数的出现次数都 \(> B\)

于是直接让出现次数 \(> B\) 的入队,对于剩下的出现次数较小的东西,两两先合并直到出现次数大了为止。

说着很抽象,具体看代码吧。复杂度 \(O(n \sqrt n \log n)\)

Code
#include <bits/stdc++.h>
#define _for(i, a, b)  for (int i = (a); i <= (b); i ++ )
#define ll long long
#define pb push_back
using namespace std;
const int N = 4e5 + 5, V = N - 5;
int n, Q, B, BV, cur[N], a[N], cnt[N], cntcnt[N], bel[N]; ll ans[N]; vector<int> vec; priority_queue<int, vector<int>, greater<int> > q;
struct Node { int l, r, id; } qry[N];
inline void upd(int x, int k) { cntcnt[cnt[x]] -- , cntcnt[cnt[x] += k] ++ ; }
inline ll gans() {
	while (q.size())  q.pop(); int pre = 0, x, y; ll res = 0;
	for (int i : vec)  if (cnt[i] > BV)  q.push(cnt[i]);
	_for (i, 1, BV)  cur[i] = cntcnt[i];
	_for (i, 1, BV)  if (cur[i]) {
		if (pre) {
			res += i + pre, cur[i] -- , cur[pre] -- ;
			if (i + pre <= BV)  cur[i + pre] ++ ;
			else  q.push(i + pre); pre = 0;
		} res += 1ll * i * (cur[i] - (cur[i] & 1));
		if (2 * i <= BV)  cur[2 * i] += cur[i] >> 1;
		else  _for (j, 1, cur[i] >> 1)  q.push(2 * i);
		if (cur[i] & 1)  pre = i;
	} if (pre)  q.push(pre);
	while (q.size() > 1)  x = q.top(), q.pop(), y = q.top(), q.pop(), res += x + y, q.push(x + y); return res;
}
signed main() {
	ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
	cin >> n, B = sqrt(n); int l = 1, r = 0;
	_for (i, 1, n)  cin >> a[i], cnt[a[i]] ++ ; cin >> Q;
	_for (i, 1, V)  if (cnt[i] > (BV = sqrt(V)))  vec.pb(i);
	_for (i, 1, n)  cnt[a[i]] -- , bel[i] = (i - 1) / B + 1;
	_for (i, 1, Q)  cin >> qry[i].l >> qry[i].r, qry[i].id = i; sort(qry + 1, qry + Q + 1, [&] (Node u, Node v) { return bel[u.l] ^ bel[v.l] ? bel[u.l] < bel[v.l] : (bel[u.l] & 1 ? u.r < v.r : u.r > v.r); });
	_for (i, 1, Q) {
		while (l > qry[i].l)  upd(a[ -- l], 1);
		while (r < qry[i].r)  upd(a[ ++ r], 1);
		while (l < qry[i].l)  upd(a[l ++ ], - 1);
		while (r > qry[i].r)  upd(a[r -- ], - 1); ans[qry[i].id] = gans();
	} _for (i, 1, Q)  cout << ans[i] << "\n";
	return 0;
}

CF1476G Minimum Difference

经典结论:每种数的出现次数的集合大小 \(\le \sqrt{n}\)

于是直接上双指针扫描即可。需要带修莫队。

CF1056H Detect Robots

根号分治,对路径长度根号分治,设阈值为 \(B\)

  • 小串对小串:将每个路径扫一遍,然后 \(O(B)\) 的时间枚举当前路径上另外一个点判断即可。复杂度 \(O(B\sum k)\)

  • 大串对其他串:类似的,大串的个数小于 \(\frac{\sum_{k}}{B}\) 个。于是对于每个大串花费 \(O(\sum k)\) 的时间枚举其他串判断即可(倒着枚举,记录前驱是否相同),复杂度 \(O(\frac{(sum_k)^2}{B})\)

\(B\) 取根号即可。

P5608 [Ynoi2013] 文化课

NB 题。具体可以去我的博客里看详细题解。

出现次数 \(\le \sqrt n\) 种。

CF1056H Detect Robots

\(S = \sum len\)

暴力判断是枚举两个点 \(u, v\),他们在两条不同的路径 \(u, a, \cdots, v\)\(v, b, \cdots v\) 中同时出现,判断 \(a\) 是否等于 \(b\) 即可。

上述做法复杂度是平方的。如果两个串的长度都 \(\le \sqrt S\),那么复杂度是对的。那大串怎么处理呢?

大串只有 \(\le \sqrt S\) 个,考虑枚举每个大串并线性判断。倒着枚举其他串,找出同时出现过的最后一个位置 \(cur\)。如果当前点 \(u\) 同时出现在两个串且并不是 \(cur\) 位置,那么判断下一个点是否相等即可。

复杂度是根号的。

Code
#include <bits/stdc++.h>
#define _for(i, a, b)  for (int i = (a); i <= (b); i ++ )
#define _all(i, a, b)  for (int i = (a); i >= (b); i -- )
#define PII pair<int, int>
#define mp make_pair
#define fi first
#define se second
#define pb push_back
using namespace std;
const int N = 4e5 + 4;
int n, m, B, cur, vis[N], pos[N]; vector<PII > vec[N];
struct Path { int len; vector<int> a; } path[N];
inline void solve() {
	cin >> n >> m, B = 0;
	_for (i, 1, n)  vec[i].clear(), vis[i] = 0;
	_for (i, 1, m)  path[i].a.resize(0);
	_for (i, 1, m) {
		cin >> path[i].len, B += path[i].len, path[i].a.assign(path[i].len + 4, 0);
		_for (j, 1, path[i].len)  cin >> path[i].a[j];
	} B = sqrt(B);
	_for (i, 1, m) {
		if (path[i].len <= B)  _for (j, 2, path[i].len)  _for (k, 1, j - 1)  vec[path[i].a[j]].pb(mp(path[i].a[k], path[i].a[k + 1]));
		else {
			_for (j, 1, n)  pos[j] = 0;
			_for (j, 1, path[i].len)  pos[path[i].a[j]] = j;
			_for (j, 1, m)  if (j > i || (j < i && path[j].len <= B)) {
				cur = 0;
				_all (k, path[j].len, 1)  if (pos[path[j].a[k]]) {
					if (pos[path[j].a[k]] > cur)  cur = pos[path[j].a[k]];
					else if (path[j].a[k + 1] ^ path[i].a[pos[path[j].a[k]] + 1])  return cout << "Human\n", void();
				}
			}
		}
	} _for (i, 1, n) {
		for (PII j : vec[i]) { if (vis[j.fi] && vis[j.fi] ^ j.se)  return cout << "Human\n", void(); vis[j.fi] = j.se; }
		for (PII j : vec[i])  vis[j.fi] = 0;
	} cout << "Robot\n";
}
signed main() {
	ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
	int T; cin >> T; while (T -- )  solve();
	return 0;
}

扫描线

P3863 序列

考虑扫描线扫序列,分块维护时间,操作是区间加,区间求一个数 rank。

P8734 [蓝桥杯 2020 国 A] 奇偶覆盖

首先空白的也是偶数,比较烦,转化为总面积减去奇数。维护奇数面积打翻转 tag 即可。

P5816 [CQOI2010] 内部白点

显然会在一轮后结束。答案即为 \(n\) 加上横线和竖线的交点个数。扫描线维护即可。

P9990 [Ynoi Easy Round 2023] TEST_90

\(r\) 扫描线,修改是 \([pre_r + 1, r]\) 翻转奇偶性,查询是历史和。矩阵维护即可。懒得卡常了。

P9991 [Ynoi Easy Round 2023] TEST_107

分三种情况讨论,扫描线即可。

P5524 [Ynoi2012] NOIP2015 充满了希望

先扫一遍操作,对于每个位置 \(i\) 维护他最后一次被覆盖的时间 \(t_i\)

询问考虑扫描线,扫到一个操作 3 \(x\)\(t_x\) 加上对应的数值,代表第 \(t_x\) 次操产生了贡献。后缀和维护。

51Nod-1559 车和矩形

简单扫描线。一个矩形被覆盖的条件是每一行都有车且每一列都有车。

扫描线扫 \(y\),线段树维护每个横坐标上的车的纵坐标最大值,区间查询最小值是否在矩形下界之上即可。


DS 模拟费用流

P9168 [省选联考 2023] 人员调度

删人是困难的,于是上线段树分治转化成加人和撤销。

转化为了如下费用流模型:

  • 源点连每个员工,流量为 \(1\),费用为 \(v_i\)

  • 每个员工连对应的树上节点,流量为 \(\infty\),费用为 \(0\)

  • 树上每个节点连向自己的儿子,流量 \(\infty\),费用为 \(0\)

  • 树上每个节点向汇点连边,流量为 \(1\),费用为 \(0\)

暴力跑费用流肯定爆炸,于是考虑模拟增广的过程,分为两种:

  • 选择子树内的一个坑(未被人占领),然后占领之;

  • 已被占满,选择权值最小的一个并替换;

用两棵线段树模拟上述过程即可,具体不赘述。复杂度是三只 log 的。

Code
#include <bits/stdc++.h>
#define _for(i, a, b)  for (int i = (a); i <= (b); i ++ )
#define _all(i, a, b)  for (int i = (a); i >= (b); i -- )
#define ll long long
#define PII pair<int, int>
#define mp make_pair
#define fi first
#define se second
#define pb push_back
using namespace std;
const int N = 4e5 + 5, INF = 1e9 + 5;
int n, k, m, ttop, dfn_cnt, sz[N], son[N], dep[N], top[N], dfn[N], rnk[N], fa[N], a[N], b[N], t[N], vis[N]; vector<int> Tr[N]; multiset<int> S[N]; ll res, ans[N];
inline void chkmin(int & x, int y) { x = x < y ? x : y; }
inline void chkmax(int & x, int y) { x = x > y ? x : y; }
struct Rollback { int a, b, c; } rollback[N];
struct Segment_Tree1 {
	#define lc (p << 1)
	#define rc (p << 1 | 1)
	#define mid ((tr[p].l + tr[p].r) >> 1)
	struct Tree { int l, r, tag, val; PII val2; } tr[N << 2];
	inline int len(int p) { return tr[p].r - tr[p].l + 1; }
	inline bool In(int p, int l, int r) { return l <= tr[p].l && tr[p].r <= r; }
	inline void push_up(int p) { tr[p].val = min(tr[lc].val, tr[rc].val); }
	inline void push_tag(int p, int k) { tr[p].val += k, tr[p].tag += k; }
	inline void push_down(int p) { if (tr[p].tag)  push_tag(lc, tr[p].tag), push_tag(rc, tr[p].tag), tr[p].tag = 0; }
	void build(int p, int l, int r) {
		tr[p].l = l, tr[p].r = r, tr[p].tag = tr[p].val = 0;
		if (l == r)  return tr[p].val2 = mp(0, l), void(); build(lc, l, mid), build(rc, mid + 1, r), push_up(p), tr[p].val2.se = tr[p].l;
	} void update(int p, int l, int r, int k) {
		if (l > r)  return ;
		if (In(p, l, r))  return push_tag(p, k); push_down(p);
		if (l <= mid)  update(lc, l, r, k);
		if (r > mid)  update(rc, l, r, k); push_up(p);
	} int query(int p, int l, int r) {
		if (l > r)  return INF;
		if (In(p, l, r))  return tr[p].val; push_down(p);
		if (r <= mid)  return query(lc, l, r);
		if (l > mid)  return query(rc, l, r); return min(query(lc, l, r), query(rc, l, r));
	} int fd(int p) { if (len(p) == 1)  return tr[p].l; push_down(p); return fd(tr[rc].val ? lc : rc); }
	int find(int p, int l, int r) {
		if (In(p, l, r))  return tr[p].val ? - 1 : fd(p); push_down(p);
		if (r <= mid)  return find(lc, l, r);
		if (l > mid)  return find(rc, l, r); int res = find(rc, l, r); return ~ res ? res : find(lc, l, r);
	} void update1(int p, int x, int k) { if (len(p) == 1)  return tr[p].val2.fi = k, void(); update1(x <= mid ? lc : rc, x, k), tr[p].val2 = max(tr[lc].val2, tr[rc].val2); }
	PII query1(int p, int l, int r) {
		if (In(p, l, r))  return tr[p].val2;
		if (r <= mid)  return query1(lc, l, r);
		if (l > mid)  return query1(rc, l, r); return max(query1(lc, l, r), query1(rc, l, r));
	}
	#undef lc
	#undef rc
	#undef mid
} SGT1;
void dfs1(int u) {
	sz[u] = 1, son[u] = - 1;
	for (int v : Tr[u])  if (v ^ fa[u]) {
		fa[v] = u, dep[v] = dep[u] + 1, dfs1(v), sz[u] += sz[v];
		if ( ! ~ son[u] || sz[son[u]] < sz[v])  son[u] = v;
	}
} void dfs2(int u, int Top) {
	rnk[dfn[u] = ++ dfn_cnt] = u, top[u] = Top;
	if ( ~ son[u])  dfs2(son[u], Top);
	for (int v : Tr[u])  if (v ^ son[u] && v ^ fa[u])  dfs2(v, v);
} inline int find(int u) { int res = SGT1.find(1, dfn[top[u]], dfn[u]); return ~ res ? rnk[res] : find(fa[top[u]]); }
inline void update(int u, int v, int k) {
	int U = top[u], V = top[v];
	while (U ^ V) {
		if (dep[U] >= dep[V])  SGT1.update(1, dfn[U], dfn[u], k), u = fa[U];
		else  SGT1.update(1, dfn[V], dfn[v], k), v = fa[V]; U = top[u], V = top[v];
	} SGT1.update(1, min(dfn[u], dfn[v]), max(dfn[u], dfn[v]), k);
} inline void add(int u, int y) { S[u].insert(y), SGT1.update1(1, dfn[u], * S[u].rbegin()), update(1, u, - 1); }
inline void del(int u, int y) { S[u].erase(S[u].find(y)), SGT1.update1(1, dfn[u], S[u].size() ? * S[u].rbegin() : - INF), update(1, u, 1); }
struct Segment_Tree2 {
	#define lc (p << 1)
	#define rc (p << 1 | 1)
	#define mid ((tr[p].l + tr[p].r) >> 1)
	struct Tree { int l, r; vector<int> upd; } tr[N << 2];
	inline int len(int p) { return tr[p].r - tr[p].l + 1; }
	inline bool In(int p, int l, int r) { return l <= tr[p].l && tr[p].r <= r; }
	void build(int p, int l, int r) {
		tr[p].l = l, tr[p].r = r;
		if (l == r)  return ; build(lc, l, mid), build(rc, mid + 1, r);
	} void update(int p, int l, int r, int k) {
		if (l > r)  return ;
		if (In(p, l, r))  return tr[p].upd.pb(k);
		if (l <= mid)  update(lc, l, r, k);
		if (r > mid)  update(rc, l, r, k);
	} void solve(int p) {
		int cur = ttop, x; PII t;
		for (int i : tr[p].upd) {
			x = find(a[i]), t = SGT1.query1(1, dfn[x], dfn[x] + sz[x] - 1);
			if (t.fi + b[i] > 0)  rollback[ ++ ttop] = (Rollback){i, t.fi, t.se}, res += t.fi + b[i], del(rnk[t.se], t.fi), add(a[i], - b[i]);
		} if (len(p) == 1)  ans[tr[p].l] = res;
		else  solve(lc), solve(rc);
		while (ttop > cur)  res -= b[rollback[ttop].a] + rollback[ttop].b, add(rnk[rollback[ttop].c], rollback[ttop].b), del(a[rollback[ttop].a], - b[rollback[ttop].a]), ttop -- ;
	}
	#undef lc
	#undef rc
	#undef mid
} SGT2;
signed main() {
	ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
	cin >> n >> n >> k >> m; int op, x, y; SGT2.build(1, 0, m), SGT1.build(1, 1, n);
	_for (i, 2, n)  cin >> fa[i], Tr[fa[i]].pb(i); dfs1(1), dfs2(1, 1);
	_for (i, 1, n)  S[i].insert(0);
	_for (i, 1, k)  cin >> a[i] >> b[i], t[i] = 0, vis[i] = 1;
	_for (i, 1, m) {
		cin >> op >> x;
		if (op == 1)  cin >> y, t[ ++ k] = i, vis[k] = 1, a[k] = x, b[k] = y;
		else  SGT2.update(1, t[x], i - 1, x), vis[x] = 0;
	} _for (i, 1, k)  if (vis[i])  SGT2.update(1, t[i], m, i); SGT2.solve(1);
	_for (i, 0, m)  cout << ans[i] << " "; cout << "\n";
	return 0;
}

分块

P2325 [SCOI2005] 王室联邦

树分块。

Code
#include <bits/stdc++.h>
#define _for(i, a, b)  for (int i = (a); i <= (b); i ++ )
#define pb push_back
using namespace std;
const int N = 4e5 + 5;
int n, B, m, top, stk[N], bel[N], rt[N]; vector<int> Tr[N];
inline void dfs(int u, int fa) {
	int cur = top;
	for (int v : Tr[u])  if (v ^ fa) {
		dfs(v, u);
		if (top - cur >= B) {
			rt[ ++ m] = u;
			while (top > cur)  bel[stk[top -- ]] = m;
		}
	} stk[ ++ top] = u;
} signed main() {
	ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
	cin >> n >> B; int u, v;
	_for (i, 2, n)  cin >> u >> v, Tr[u].pb(v), Tr[v].pb(u); dfs(1, 0);
	if ( ! m)  rt[ ++ m] = 1;
	while (top)  bel[stk[top -- ]] = m; cout << m << "\n";
	_for (i, 1, n)  cout << bel[i] << " "; cout << "\n";
	_for (i, 1, m)  cout << rt[i] << " "; cout << "\n";
	return 0;
}

P5048 [Ynoi2019 模拟赛] Yuno loves sqrt technology III

P4168 蒲公英 一样,区间众数强制在线。唯一的区别是该题卡空间。

仍然考虑对序列分块,然后预处理 \(f_{i, j}\) 表示第 \(i\) 到第 \(j\) 块的众数个数。这一部分时间 \(O(n \sqrt n)\),空间 \(O(n)\)

对于每一个值 \(i\),对其预处理一个 vector 存储其出现的位置 \(vec_i\),然后对于每一个位置 \(i\) 也存一下 \(i\)\(vec_{a_i}\) 中排名 \(pos_i\)

考虑对于一个查询 \([l, r]\),整块的答案直接查表,设为 \(k\)。然后扫左右的散块。如果 \(vec_{a_i, pos_i + k} \le r\) 则不断 \(k \leftarrow k + 1\)。最终的 \(k\) 即为答案。意义是显然的。

每个询问是 \(O(\sqrt n)\) 的,总复杂度 \(O((n + q) \sqrt n)\)

Code
#include <bits/stdc++.h>
#define _for(i, a, b)  for (int i = (a); i <= (b); i ++ )
using namespace std;
const int N = 5e5 + 5, M = 720;
int n, q, B, m, ans, L[M], R[M], cnt[N], bel[N], a[N], pos[N], f[M][M]; vector<int> val[N];
inline void chkmax(int & x, int y) { x = x > y ? x : y; }
inline int query(int l, int r) {
	if (bel[l] == bel[r]) {
		int res = 0;
		_for (i, l, r)  chkmax(res, ++ cnt[a[i]]);
		_for (i, l, r)  cnt[a[i]] -- ; return res;
	} int res = f[bel[l] + 1][bel[r] - 1];
	_for (i, l, R[bel[l]])  while (pos[i] + res < (int)val[a[i]].size() && val[a[i]][pos[i] + res] <= r)  res ++ ;
	_for (i, L[bel[r]], r)  while (pos[i] - res >= 0 && val[a[i]][pos[i] - res] >= l)  res ++ ; return res;
} signed main() {
	ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
	cin >> n >> q; int l, r; m = (n - 1) / (B = (int)sqrt(n)) + 1;
	_for (i, 1, n)  cin >> a[i], pos[i] = val[a[i]].size(), val[a[i]].push_back(i), bel[i] = (i - 1) / B + 1;
	_for (i, 1, m)  L[i] = (i - 1) * B + 1, R[i] = min(i * B, n);
	_for (i, 1, m) {
		_for (j, i, m) {
			if (i ^ j)  f[i][j] = f[i][j - 1];
			_for (k, L[j], R[j])  chkmax(f[i][j], ++ cnt[a[k]]);
		} _for (j, L[i], n)  cnt[a[j]] -- ;
	} while (q -- )  cin >> l >> r, l ^= ans, r ^= ans, cout << (ans = query(min(l, r), max(l, r))) << "\n";
	return 0;
}

P4135 作诗

分块。

我是用的 数的个数 减去 出现奇数次的数的个数 来算的。

强制在线的区间数颜色可以考虑预处理 \(f_{l, r}\) 表示第 \(l\) 个块到第 \(r\) 个块的不同数的个数 以及 \(occ_{i, j}\) 表示前 \(i\) 个块中 \(j\) 这个数出现了多少次。同理出现奇数次的数做同样的预处理。

然后查询就非常简单了:在整块的基础上把散块的贡献计算一下即可。复杂度是 \(O(n \sqrt n)\)

Code
#include <bits/stdc++.h>
#define _for(i, a, b)  for (int i = (a); i <= (b); i ++ )
using namespace std;
const int N = 1e5 + 5, M = 320;
int n, c, q, m, B, ans, a[N], L[M], R[M], bel[N], f[2][M][M], cnt[N], occ[M][N];
inline int calc(int l, int r, int x) { return l <= r ? occ[r][x] - occ[l - 1][x] : 0; }
inline int query(int l, int r) {
	if (l > r)  swap(l, r); int bl = bel[l], br = bel[r];
	if (bl == br) {
		int res = 0;
		_for (i, l, r)  cnt[a[i]] ++ ;
		_for (i, l, r)  if (cnt[a[i]] && ! (cnt[a[i]] & 1))  res ++ , cnt[a[i]] = 0;
		_for (i, l, r)  cnt[a[i]] = 0; return res;
	} int res1 = f[0][bl + 1][br - 1], res2 = f[1][bl + 1][br - 1];
	_for (i, l, R[bl])  res1 += ! cnt[a[i]] && ! calc(bl + 1, br - 1, a[i]), cnt[a[i]] ++ ;
	_for (i, L[br], r)  res1 += ! cnt[a[i]] && ! calc(bl + 1, br - 1, a[i]), cnt[a[i]] ++ ;
	_for (i, l, R[bl])  if (cnt[a[i]] & 1)  res2 += calc(bl + 1, br - 1, a[i]) & 1 ? - 1 : 1, cnt[a[i]] = 0;
	_for (i, L[br], r)  if (cnt[a[i]] & 1)  res2 += calc(bl + 1, br - 1, a[i]) & 1 ? - 1 : 1, cnt[a[i]] = 0;
	_for (i, l, R[bl])  cnt[a[i]] = 0;
	_for (i, L[br], r)  cnt[a[i]] = 0; return res1 - res2;
} signed main() {
	ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
	cin >> n >> c >> q, m = (n - 1) / (B = (int)sqrt(n)) + 1; int l, r;
	_for (i, 1, n)  cin >> a[i], bel[i] = (i - 1) / B + 1;
	_for (i, 1, m)  L[i] = (i - 1) * B + 1, R[i] = min(n, i * B);
	_for (i, 1, m) {
		_for (j, L[i], R[i])  occ[i][a[j]] ++ ;
		_for (j, 1, c)  occ[i][j] += occ[i - 1][j];
		_for (j, i, m) {
			if (i ^ j)  f[0][i][j] = f[0][i][j - 1], f[1][i][j] = f[1][i][j - 1];
			_for (k, L[j], R[j]) {
				cnt[a[k]] ++ ;
				if (cnt[a[k]] == 1)  f[0][i][j] ++ ;
				if (cnt[a[k]] & 1)  f[1][i][j] ++ ;
				else  f[1][i][j] -- ;
			}
		} _for (j, L[i], n)  cnt[a[j]] -- ;
	} while (q -- )  cin >> l >> r, cout << (ans = query((l + ans) % n + 1, (r + ans) % n + 1)) << "\n";
	return 0;
}

P5692 [MtOI2019] 手牵手走向明天

分块。是 P5397 [Ynoi2018] 天降之物 的区间修改 / 查询版本。

具体不想讲了。

Code
#include <bits/stdc++.h>
#define _for(i, a, b)  for (register int i = (a); i <= (b); i ++ )
#define _all(i, a, b)  for (register int i = (a); i >= (b); i -- )
using namespace std;
namespace IO {
	const int LIM = 1e6; static char buf[LIM], * p1 = buf, * p2 = buf, obuf[LIM], * p3 = obuf, cc[20];
	#define gc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, LIM, stdin), p1 == p2) ? EOF : * p1 ++ )
	#define pc(x) (p3 - obuf < LIM) ? ( * p3 ++ = x) : (fwrite(obuf, p3 - obuf, 1, stdout), p3 = obuf, * p3 ++ = x)
	inline int rd() {
		int val = 0, sgn = 1; char ch = gc();
		while ( ! isdigit(ch)) { if (ch == '-')  sgn = - 1; ch = gc(); }
		while (isdigit(ch))  val = (val << 3) + (val << 1) + (ch ^ 48), ch = gc();
		return (val * sgn);
	} inline void wt(int x, char c) { 
		if ( ! x)  pc('0');
		int len = 0;
		if (x < 0)  x = - x, pc('-');
		while (x)  cc[len ++ ] = x % 10 | '0', x /= 10;
		while (len -- )  pc(cc[len]); pc(c);
	}
} using namespace IO;
const int N = 1e5 + 1, M = 401, K = 401, INF = 1e9 + 5;
int n, q, B, m, pos[N], a[N], vis[N], b[K], p[N], len[N], bel[N], L[K], R[K], rk[N][K], st[M][K], ed[M][K], dis[K][M][M];
inline void chkmin(int & x, int y) { x = x < y ? x : y; }
inline void chkmax(int & x, int y) { x = x > y ? x : y; }
inline void init(int id) {
	int t = 0;
	_for (i, L[id], R[id])  b[ ++ t] = a[i]; sort(b + 1, b + t + 1), t = unique(b + 1, b + t + 1) - b - 1, len[id] = t;
	_for (i, 1, t)  pos[b[i]] = i;
	_for (i, L[id], R[id])  p[i] = rk[a[i]][id] = pos[a[i]], ed[rk[a[i]][id]][id] = i;
	_all (i, R[id], L[id])  st[rk[a[i]][id]][id] = i;
	_for (i, 1, t)  _for (j, i + 1, t)  dis[id][i][j] = dis[id][j][i] = INF;
	_for (i, L[id], R[id])  _for (j, i + 1, R[id])  chkmin(dis[id][rk[a[i]][id]][rk[a[j]][id]], j - i), chkmin(dis[id][rk[a[j]][id]][rk[a[i]][id]], j - i);
}
inline void adjust(int b, int x) {
	int lst = 0;
	_for (i, 1, len[b])  dis[b][x][i] = dis[b][i][x] = INF;
	_for (i, L[b], R[b]) {
		if (p[i] == x)  lst = i, ed[x][b] = i;
		else if (lst)  chkmin(dis[b][x][p[i]], i - lst), chkmin(dis[b][p[i]][x], i - lst);
	} lst = 0;
	_all (i, R[b], L[b]) {
		if (p[i] == x)  lst = i, st[x][b] = i;
		else if (lst)  chkmin(dis[b][x][p[i]], lst - i), chkmin(dis[b][p[i]][x], lst - i);
	}
}
inline void update_bf(int l, int r, int x, int y) {
	int b = bel[l];
	if ( ! rk[x][b])  return ;
	if ( ! rk[y][b]) {
		_for (i, L[b], R[b])  vis[p[i]] = 1;
		_for (i, 1, len[b] + 1)  if ( ! vis[i]) { rk[y][b] = i; break; }
		_for (i, L[b], R[b])  vis[p[i]] = 0;
	}
	_for (i, l, r)  if (p[i] == rk[x][b])  p[i] = rk[y][b]; int cnt = 0;
	_for (i, L[b], R[b])  cnt += p[i] == rk[x][b];
	if ( ! cnt)  rk[x][b] = 0; cnt = 0;
	_for (i, L[b], R[b])  cnt += p[i] == rk[y][b];
	if ( ! cnt)  rk[y][b] = 0; len[b] = 0;
	_for (i, L[b], R[b])  chkmax(len[b], p[i]);
	if (len[b] > B) {
		int pos = 0;
		_for (i, L[b], R[b])  vis[p[i]] = 1;
		_for (i, 1, B)  if ( ! vis[i]) { pos = i; break; }
		_for (i, L[b], R[b]) {
			vis[p[i]] = 0;
			if (p[i] == len[b])  p[i] = pos;
		} if (rk[x][b] == len[b])  rk[x][b] = pos;
		if (rk[y][b] == len[b])  rk[y][b] = pos; len[b] -- ;
	}
	if (rk[x][b])  adjust(b, rk[x][b]);
	if (rk[y][b])  adjust(b, rk[y][b]);
}
inline void update(int l, int r, int x, int y) {
	if (x == y)  return ; int bl = bel[l], br = bel[r];
	if (bl == br)  return update_bf(l, r, x, y); update_bf(l, R[bl], x, y), update_bf(L[br], r, x, y);
	_for (i, bl + 1, br - 1)  if (rk[x][i]) {
		if (rk[y][i]) {
			_for (j, L[i], R[i])  p[j] = p[j] == rk[x][i] ? rk[y][i] : p[j]; chkmin(st[rk[y][i]][i], st[rk[x][i]][i]), chkmax(ed[rk[y][i]][i], ed[rk[x][i]][i]);
			_for (j, 1, len[i])  chkmin(dis[i][rk[y][i]][j], dis[i][rk[x][i]][j]), chkmin(dis[i][j][rk[y][i]], dis[i][rk[x][i]][j]); rk[x][i] = 0;
		} else  rk[y][i] = rk[x][i], rk[x][i] = 0;
	}
}
inline int query_bf(int l, int r, int x, int y) {
	int b = bel[l], lst_x = 0, lst_y = 0, ans = INF;
	if ( ! rk[x][b] || ! rk[y][b])  return INF;
	_for (i, l, r) {
		if (p[i] == rk[x][b])  lst_x = i;
		if (p[i] == rk[y][b])  lst_y = i;
		if (lst_x && lst_y)  chkmin(ans, abs(lst_x - lst_y));
	} return ans;
}
inline int query(int l, int r, int x, int y) {
	int lst_x = 0, lst_y = 0, bl = bel[l], br = bel[r], ans;
	if (bl == br)  return query_bf(l, r, x, y); ans = min(query_bf(l, R[bl], x, y), query_bf(L[br], r, x, y)), lst_x = rk[x][bl] && ed[rk[x][bl]][bl] >= l ? ed[rk[x][bl]][bl] : 0, lst_y = rk[y][bl] && ed[rk[y][bl]][bl] >= l ? ed[rk[y][bl]][bl] : 0;
	_for (i, bl + 1, br - 1) {
		if (rk[x][i] && x == y)  return 0;
		if (rk[x][i] && rk[y][i])  chkmin(ans, dis[i][rk[x][i]][rk[y][i]]);
		if (lst_x && rk[y][i])  chkmin(ans, st[rk[y][i]][i] - lst_x);
		if (lst_y && rk[x][i])  chkmin(ans, st[rk[x][i]][i] - lst_y); chkmax(lst_x, ed[rk[x][i]][i]), chkmax(lst_y, ed[rk[y][i]][i]);
		if (ans == 1)  break;
	} if (lst_x)  chkmin(ans, (rk[y][br] && st[rk[y][br]][br] <= r ? st[rk[y][br]][br] : INF) - lst_x);
	if (lst_y)  chkmin(ans, (rk[x][br] && st[rk[x][br]][br] <= r ? st[rk[x][br]][br] : INF) - lst_y); return ans;
}
int main() {
	ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
	n = rd(), q = rd(), B = sqrt(n); int op, l, r, x, y, ans;
	_for (i, 1, n)  a[i] = rd(), bel[i] = (i - 1) / B + 1; m = ceil(n * 1.0 / B);
	_for (i, 1, m)  L[i] = (i - 1) * B + 1, R[i] = min(n, i * B), init(i);
	while (q -- ) {
		op = rd(), l = rd(), r = rd(), x = rd(), y = rd();
		if (op == 1)  update(l, r, x, y);
		else  wt((ans = query(l, r, x, y)) <= n ? ans : - 1, '\n');
	}
	return fwrite(obuf, p3 - obuf, 1, stdout), 0;
}

杂项 / Ad-hoc

P11306 [COTS 2016] 搜索树 Jelka

考虑把树中序遍历,这样一棵子树仍然是一段连续的区间,而一个子树合法当且仅当这段区间是不降的。

于是查询时简单的:线段维护 dfn 序相邻两项之差,区间 \(\min \ge 0\) 即合法。

修改的话需要倍增求出需要修改的最顶上的位置,然后区间赋值,需要单点改线段树。

复杂度 \(O(n \log^2 n)\)。卡常素质低下。

Code
#include <bits/stdc++.h>
#define _for(i, a, b)  for (int i = (a); i <= (b); i ++ )
#define _all(i, a, b)  for (int i = (a); i >= (b); i -- )
#define pb push_back
using namespace std;
namespace IO {
	const int LIM = 1e5;
	static char buf[LIM], * p1 = buf, * p2 = buf, obuf[LIM], * p3 = obuf, cc[20];
	#define gc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, LIM, stdin), p1 == p2) ? EOF : * p1 ++ )
	#define pc(x) (p3 - obuf < LIM) ? ( * p3 ++ = x) : (fwrite(obuf, p3 - obuf, 1, stdout), p3 = obuf, * p3 ++ = x)
	inline int rd() {
		int val = 0, sgn = 1; char ch = gc();
		for ( ; ch < '0' || ch > '9'; ch = gc())  if (ch == '-')  sgn = - 1;
		for ( ; ch >= '0' && ch <= '9'; ch = gc())  val = (val << 3) + (val << 1) + (ch ^ 48); return (val * sgn);
	} inline void wt(int x, char c) {
		if ( ! x)  pc('0'); int len = 0;
		if (x < 0)  x = - x, pc('-');
		while (x)  cc[len ++ ] = x % 10 | '0', x /= 10;
		while (len -- )  pc(cc[len]); pc(c);
	}
} using namespace IO;
const int N = 2e5 + 5, INF = 2e9 + 5;
int n, q, D, ans, dfn_cnt, dfn_cnt2, dd[N], sz[N], son[N], dep[N], top[N], dfn[N], rnk[N], fa[N], a[N], L[N], R[N], ls[N], rs[N], dfn2[N], rnk2[N], UP[N][20]; vector<int> Tr[N];
struct Node { int mi, ma, ok; };
inline Node operator + (Node u, Node v) { return (Node){min(u.mi, v.mi), max(u.ma, v.ma), u.ok & v.ok & (u.ma <= v.mi)}; }
struct Segment_Tree {
	#define lc (p << 1)
	#define rc (p << 1 | 1)
	#define mid ((tr[p].l + tr[p].r) >> 1)
	struct Tree { int l, r, val; } tr[N << 2];
	inline int len(int p) { return tr[p].r - tr[p].l + 1; }
	inline bool In(int p, int l, int r) { return l <= tr[p].l && tr[p].r <= r; }
	inline void push_up(int p) { tr[p].val = min(tr[lc].val, tr[rc].val); }
	void build(int p, int l, int r) {
		tr[p].l = l, tr[p].r = r;
		if (l == r)  return tr[p].val = a[rnk2[l]] - a[rnk2[l - 1]], void(); build(lc, l, mid), build(rc, mid + 1, r), push_up(p);
	} void update(int p, int x, int k) { if (x < 1 || x > n)  return ; if (len(p) == 1)  return tr[p].val = k, void(); update(x <= mid ? lc : rc, x, k), push_up(p); }
	int query(int p, int l, int r) {
		if (l > r)  return INF;
		if (In(p, l, r))  return tr[p].val;
		if (r <= mid)  return query(lc, l, r);
		if (l > mid)  return query(rc, l, r); return min(query(lc, l, r), query(rc, l, r));
	}
	#undef lc
	#undef rc
	#undef mid
} SGT;
void dfs(int u) {
	if (ls[u])  dfs(ls[u]); rnk2[dfn2[u] = ++ dfn_cnt2] = u, L[u] = L[ls[u]] ? L[ls[u]] : dfn2[u];
	if (rs[u])  dfs(rs[u]); R[u] = R[rs[u]] ? R[rs[u]] : dfn2[u];
} void dfs3(int u) {
	sz[u] = 1, son[u] = - 1, UP[u][0] = fa[u] ? fa[u] : u;
	_for (i, 1, D)  UP[u][i] = UP[UP[u][i - 1]][i - 1];
	for (int v : Tr[u])  if (v ^ fa[u]) {
		fa[v] = u, dep[v] = dep[u] + 1, dd[v] = log2(dep[v]), dfs3(v), sz[u] += sz[v];
		if ( ! ~ son[u] || sz[son[u]] < sz[v])  son[u] = v;
	}
} void dfs4(int u, int Top) {
	rnk[dfn[u] = ++ dfn_cnt] = u, top[u] = Top;
	if ( ~ son[u])  dfs4(son[u], Top);
	for (int v : Tr[u])  if (v ^ son[u] && v ^ fa[u])  dfs4(v, v);
} inline void upd(int u, int y) {
	int U = u, U2 = u;
	if (SGT.query(1, L[u] + 1, R[u]) >= 0) {
		_all (i, dd[u], 0) {
			if (SGT.query(1, L[UP[U][i]] + 1, R[UP[U][i]]) >= 0)  U = UP[U][i];
			if (U == 1)  break;
		} SGT.update(1, dfn2[u], y - a[rnk2[dfn2[u] - 1]]), SGT.update(1, dfn2[u] + 1, a[rnk2[dfn2[u] + 1]] - y);
		if (SGT.query(1, L[u] + 1, R[u]) < 0)  ans -= dep[u] - dep[U] + 1;
		else {
			_all (i, dd[u], 0) {
				if (SGT.query(1, L[UP[U2][i]] + 1, R[UP[U2][i]]) >= 0)  U2 = UP[U2][i];
				if (U2 == 1)  break;
			} if (dep[U2] < dep[U])  ans += dep[U] - dep[U2];
			else if (dep[U2] > dep[U])  ans -= dep[U2] - dep[U];
		}
	} else {
		SGT.update(1, dfn2[u], y - a[rnk2[dfn2[u] - 1]]), SGT.update(1, dfn2[u] + 1, a[rnk2[dfn2[u] + 1]] - y);
		_all (i, dd[u], 0) {
			if (SGT.query(1, L[UP[U][i]] + 1, R[UP[U][i]]) >= 0)  U = UP[U][i];
			if (U == 1)  break;
		} if (SGT.query(1, L[u] + 1, R[u]) >= 0)  ans += dep[u] - dep[U] + 1;
	} a[u] = y;
} signed main() {
	ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
	n = rd(), q = rd(), D = floor(log2(n)); int x, y;
	_for (i, 1, n) {
		ls[i] = rd(), rs[i] = rd();
		if (ls[i])  Tr[i].pb(ls[i]);
		if (rs[i])  Tr[i].pb(rs[i]);
	} _for (i, 1, n)  a[i] = rd(); dfs(1), SGT.build(1, 1, n), dfs3(1), dfs4(1, 1);
	_for (i, 1, n)  ans += SGT.query(1, L[i] + 1, R[i]) >= 0;
	while (q -- )  x = rd(), y = rd(), upd(x, y), wt(ans, '\n');
	return fwrite(obuf, p3 - obuf, 1, stdout), 0;
}

P11163 [BalkanOI 2023] Weights

显然一个点 \(u\) 的答案为 \(\max_{v \in \text{subtree}(u)} 2^{dep_v - dep_u}w_v\)

直接线段树维护即可。判断 \(a2^x\)\(b2^y\) 的大小可以同时对 \(2\)\(\log\)

Code
#include <bits/stdc++.h>
#define _for(i, a, b)  for (int i = (a); i <= (b); i ++ )
#define _all(i, a, b)  for (int i = (a); i >= (b); i -- )
#define ll long long
#define PII pair<int, int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
using namespace std;
const int N = 4e5 + 5, P = 998244353, INF = 1e9 + 5;
int n, q, dfn_cnt, w[N], sz[N], dep[N], dfn[N], rnk[N], fa[N]; PII val[N]; vector<int> Tr[N];
inline int mul(int x, int y) { return 1ll * x * y % P; }
inline void Mul(int & x, int y) { x = 1ll * x * y % P; }
inline int qpow(int x, int y) {
	int res = 1;
	for ( ; y; y >>= 1, Mul(x, x))  if (y & 1)  Mul(res, x); return res;
} inline bool operator < (PII u, PII v) { return log2(u.fi) + u.se < log2(v.fi) + v.se; }
inline bool operator != (PII u, PII v) { return u.fi ^ v.fi || u.se ^ v.se; }
inline PII min(PII u, PII v) { return u < v ? u : v; }
inline PII max(PII u, PII v) { return u < v ? v : u; }
inline void chkmax(PII & u, PII v) { u = u < v ? v : u; }
struct Segment_Tree {
	#define lc (p << 1)
	#define rc (p << 1 | 1)
	#define mid ((tr[p].l + tr[p].r) >> 1)
	struct Tree { int l, r; PII t; } tr[N << 2];
	inline int len(int p) { return tr[p].r - tr[p].l + 1; }
	inline bool In(int p, int l, int r) { return l <= tr[p].l && tr[p].r <= r; }
	inline void push_up(int p) { tr[p].t = max(tr[lc].t, tr[rc].t); }
	void build(int p, int l, int r) {
		tr[p].l = l, tr[p].r = r, tr[p].t = mp(1, 0);
		if (l == r)  return tr[p].t = val[rnk[tr[p].l]], void(); build(lc, l, mid), build(rc, mid + 1, r), push_up(p);
	} void update(int p, int x, int k) { if (len(p) == 1)  return tr[p].t.fi = k, void(); update(x <= mid ? lc : rc, x, k), push_up(p); }
	PII query(int p, int l, int r) {
		if (In(p, l, r))  return tr[p].t; PII res = mp(1, 0);
		if (l <= mid)  chkmax(res, query(lc, l, r));
		if (r > mid)  chkmax(res, query(rc, l, r)); return res;
	}
	#undef lc
	#undef rc
	#undef mid
} SGT;
void dfs(int u) {
	rnk[dfn[u] = ++ dfn_cnt] = u, sz[u] = 1;
	for (int v : Tr[u])  if (v ^ fa[u])  fa[v] = u, dep[v] = dep[u] + 1, dfs(v), sz[u] += sz[v]; val[u] = u > n ? mp(w[u - n], dep[u]) : mp(1, 0);
} signed main() {
	ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
	cin >> n >> q; char c1, c2; int u, v, x, y, op; PII ans;
	_for (i, 1, n)  cin >> c1 >> x >> c2 >> y, x += (c1 == 'W') * n, y += (c2 == 'W') * n, Tr[i].pb(x), Tr[i].pb(y);
	_for (i, 1, n + 1)  cin >> w[i]; dfs(1), SGT.build(1, 1, n << 1 | 1);
	while (q -- ) {
		cin >> op >> u;
		if (op == 1)  u += n, cin >> v, SGT.update(1, dfn[u], v);
		else  ans = SGT.query(1, dfn[u], dfn[u] + sz[u] - 1), cout << mul(ans.fi, qpow(2, ans.se - dep[u])) << "\n";
	}
	return 0;
}

P12014 [Ynoi April Fool's Round 2025] 牢帽

非常牛逼的愚人节题目。

先考虑 \(x = 0\):相当于是直接维护连通块信息。前三个操作需要线段树分治转化为加入和撤销。

然后如果 \(u \mid x\),我们可以把形如 \(\prod (a_i + x)\) 的式子拆开,发现是 \(b_0 + b_1x + b_1x^2 + b_2x^3 + \cdots\) 的关于 \(x\) 的多项式。由于 \(v \le 4\)\(x\)\(\ge 4\) 次幂的项对 \(u^v\) 取模后一定为 \(0\),所以只需要记前面 \(4\) 项的系数即可。这个显然是可以直接维护的。

最后 \(u \nmid x\),将 \(x\) 写成 \(pu + q\),式子变为 \(\prod (a_i + q + pu)\)。由于 \(q < u \le 10\),所以我们只需对于每个 \(q = [0, 9]\) 维护答案即可。

复杂度是 \(O((n + q) \log q)\) 的,带几十的常数。

Code
#include <bits/stdc++.h>
#define _for(i, a, b)  for (int i = (a); i <= (b); i ++ )
#define _all(i, a, b)  for (int i = (a); i >= (b); i -- )
#define ll long long
#define ld long double
#define PII pair<int, int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
using namespace std;
const int N = 4e5 + 5;
int n, q, u, v, P = 1, top, top2, qry[N], a[N], isq[N], lst[N]; map<PII, int> vis, ok;
inline void chkmin(int & x, int y) { x = x < y ? x : y; }
inline void chkmax(int & x, int y) { x = x > y ? x : y; }
inline int add(int x, int y) { return x + y >= P ? x + y - P : x + y; }
inline int sub(int x, int y) { return x - y < 0 ? x - y + P : x - y; }
inline int mul(int x, int y) { return 1ll * x * y % P; }
inline void Add(int & x, int y) { x = x + y >= P ? x + y - P : x + y; }
inline void Sub(int & x, int y) { x = x - y < 0 ? x - y + P : x - y; }
inline void Mul(int & x, int y) { x = 1ll * x * y % P; }
struct Poly {
	int val[10][5];
	Poly() { memset(val, 0, sizeof(val)); }
	inline Poly operator - (Poly u) {
		Poly res;
		_for (o, 0, 9)  _for (i, 0, 3)  res.val[o][i] = sub(val[o][i], u.val[o][i]); return res;
	} inline void operator -= (Poly u) { * this = * this - u; }
	inline Poly operator + (Poly u) {
		Poly res;
		_for (o, 0, 9)  _for (i, 0, 3)  res.val[o][i] = add(val[o][i], u.val[o][i]); return res;
	} inline void operator += (Poly u) { * this = * this + u; }
	inline Poly operator * (Poly u) {
		Poly res;
		_for (o, 0, 9)  _for (i, 0, 3)  _for (j, 0, i)  Add(res.val[o][i], mul(val[o][j], u.val[o][i - j])); return res;
	} inline void operator *= (Poly u) { * this = * this * u; }
	inline Poly operator * (int u) {
		Poly res;
		_for (o, 0, 9)  _all (i, 3, 0)  res.val[o][i] = add(mul(val[o][i], add(u, o)), i ? val[o][i - 1] : 0); return res;
	} inline void operator *= (int u) { * this = * this * u; }
	inline int calc(int o, int x) {
		int res = 0, cur = 1;
		_for (i, 0, 3)  Add(res, mul(cur, val[o][i])), Mul(cur, x); return res;
	}
} cur_ans, ans[N]; struct Node { int u, v, sz; Poly val, cans; } del[N]; pair<int, Poly> rb[N];
struct DSU {
	int fa[N], sz[N]; Poly f[N];
	inline void init() { _for (i, 1, n) { _for (o, 0, 9)  f[i].val[o][0] = 1; fa[i] = i, sz[i] = 1; } }
	inline int find(int u) { while (fa[u] ^ u)  u = fa[u]; return u; }
	inline void merge(int u, int v) {
		u = find(u), v = find(v);
		if (sz[u] > sz[v])  swap(u, v);
		if (u ^ v)  del[ ++ top] = (Node){u, v, sz[v], f[v], cur_ans}, cur_ans -= f[u], cur_ans -= f[v], f[v] *= f[u], cur_ans += f[v], fa[u] = v, sz[v] += sz[u];
	} inline void rollback() { fa[del[top].u] = del[top].u, sz[del[top].v] = del[top].sz, f[del[top].v] = del[top].val, cur_ans = del[top].cans, top -- ; }
} DSU;
struct Segment_Tree {
	#define lc (p << 1)
	#define rc (p << 1 | 1)
	#define mid ((tr[p].l + tr[p].r) >> 1)
	struct Tree { int l, r; vector<PII > vec1, vec2; } tr[N << 2];
	inline int len(int p) { return tr[p].r - tr[p].l + 1; }
	inline bool In(int p, int l, int r) { return l <= tr[p].l && tr[p].r <= r; }
	void build(int p, int l, int r) {
		tr[p].l = l, tr[p].r = r;
		if (l ^ r)  build(lc, l, mid), build(rc, mid + 1, r);
	} void update1(int p, int l, int r, PII k) {
		if (l > r)  return ;
		if (In(p, l, r))  return tr[p].vec1.pb(k);
		if (l <= mid)  update1(lc, l, r, k);
		if (r > mid)  update1(rc, l, r, k);
	} void update2(int p, int l, int r, PII k) {
		if (l > r)  return ;
		if (In(p, l, r))  return tr[p].vec2.pb(k);
		if (l <= mid)  update2(lc, l, r, k);
		if (r > mid)  update2(rc, l, r, k);
	} void solve(int p) {
		int cur = top, cur2 = top2; Poly cans = cur_ans;
		for (PII i : tr[p].vec1)  DSU.merge(i.fi, i.se);
		for (PII i : tr[p].vec2)  rb[ ++ top2] = mp(DSU.find(i.fi), DSU.f[DSU.find(i.fi)]), cur_ans -= rb[top2].se, DSU.f[DSU.find(i.fi)] *= i.se, cur_ans += DSU.f[DSU.find(i.fi)];
		if (len(p) == 1)  ans[tr[p].l] = cur_ans;
		else  solve(lc), solve(rc);
		while (top2 > cur2)  DSU.f[rb[top2].fi] = rb[top2].se, top2 -- ; cur_ans = cans;
		while (top > cur)  DSU.rollback();
	}
	#undef lc
	#undef rc
	#undef mid
} SGT;
signed main() {
	ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
	cin >> n >> q >> u >> v; int op, x, y; DSU.init(), SGT.build(1, 0, q);
	_for (i, 1, n)  cin >> a[i], lst[i] = 0;
	_for (i, 1, v)  P *= u;
	_for (o, 0, 9)  cur_ans.val[o][0] = n % P;
	_for (i, 1, n)  a[i] %= P;
	_for (i, 1, q) {
		cin >> op >> x;
		if (op == 1)  cin >> y, vis[mp(x, y)] = vis[mp(y, x)] = i;
		else if (op == 2)  cin >> y, SGT.update1(1, vis[mp(x, y)], i - 1, mp(x, y)), vis[mp(x, y)] = vis[mp(y, x)] = 0;
		else if (op == 3)  cin >> y, SGT.update2(1, lst[x], i - 1, mp(x, a[x])), a[x] = y % P, lst[x] = i;
		else  isq[i] = 1, qry[i] = x;
	} for (map<PII, int> :: iterator it = vis.begin(); it != vis.end(); it ++ )  if (it -> se && ! ok[it -> fi])  SGT.update1(1, it -> se, q, it -> fi), ok[mp((it -> fi).se, (it -> fi).fi)] = 1;
	_for (i, 1, n)  SGT.update2(1, lst[i], q, mp(i, a[i])); SGT.solve(1);
	_for (i, 1, q)  if (isq[i])  cout << ans[i].calc(qry[i] % u, qry[i] / u * u) << "\n";
	return 0;
}

P7712 [Ynoi2077] hlcpq

如果直接主席树优化建图,那么显然会使答案出现错误(虚点有可能成为割点)。

考虑模拟 Tarjan 的过程。

void tarjan(int u, int is_rt) {
	dfn[u] = low[u] = ++ dfn_cnt;
	int child = 0;
	for (int v : G[u]) {
		if ( ! dfn[v]) {
			child ++ , tarjan(v, 0), low[u] = min(low[u], low[v]);
			if ( ! is_rt && low[v] >= dfn[u])  is_cut[u] = 1;
		} else  low[u] = min(low[u], dfn[v]);
	} if (is_rt && child >= 2)  is_cut[u] = 1;
}

我们在一个点 \(u\),枚举其所有出边 \(v\),不断更新 \(u\) 能到达的点中 dfn 最小的。如果 \(v\) 未被访问过,则进入 \(v\)。最后根据 dfn 以及是否是遍历的根来判断其是否是割点。

考虑主席树优化。我们不把图建出来,而是用主席树维护每个线段能到达的线段。线段树上每个节点维护 \(cnt\)\(val\) 表示 子树内有多少个还未被访问过的点子树内的线段的 dfn 的 min

由于 if ( ! dfn[v]) 只会进入 \(O(n)\) 次,我们对于一个 \(u\) 可以在主席树上 \(O(\log n)\) 求出第一个未被访问过的节点(如果 \(cnt > 0\) 则递归下去找,否则停止)。

但是你不能暴力执行 else,因为执行次数是边数级别(\(O(n^2)\))的。

观察一下性质:每个点都满足 \(low_u \le dfn_u\),那么我们用全局 \(dfn\) 的最小值来更新 \(low_u\) 是对的。

所以总复杂度是 \(O(n \log n)\) 的。

Code
#include <bits/stdc++.h>
#define _for(i, a, b)  for (int i = (a); i <= (b); i ++ )
#define pb push_back
using namespace std;
const int N = 4e5 + 5, INF = 1e9 + 5;
int n, dfn_cnt = 1, id[N], L[N], R[N], dfn[N], low[N], rt[N], ans[N]; vector<int> ins[N], del[N];
inline void chkmin(int & x, int y) { x = x < y ? x : y; }
struct Segment_Tree {
	#define lc tr[p].ls
	#define rc tr[p].rs
	#define mid ((l + r) >> 1)
	int ncnt = 0; struct Tree { int ls, rs, cnt, val; } tr[N << 5];
	inline int clone(int p) { return tr[ ++ ncnt] = tr[p], ncnt; }
	inline void push_up(int p) { tr[p].cnt = tr[lc].cnt + tr[rc].cnt, tr[p].val = min(tr[lc].val, tr[rc].val); }
	void update(int & p, int l, int r, int x, int k) {
		p = clone(p), tr[p].cnt += k;
		if (l == r)  return id[x] = k > 0 ? p : id[x], void(); x <= mid ? update(lc, l, mid, x, k) : update(rc, mid + 1, r, x, k);
	} int query(int p, int l, int r, int L, int R) {
		if (L > R || ! p)  return INF;
		if (L <= l && r <= R)  return tr[p].val; int res = INF;
		if (L <= mid)  chkmin(res, query(lc, l, mid, L, R));
		if (R > mid)  chkmin(res, query(rc, mid + 1, r, L, R)); return res;
	} int locate(int p, int l, int r, int L, int R) {
		if ( ! tr[p].cnt)  return 0;
		if (l == r)  return tr[p].cnt = 0, tr[p].val = ++ dfn_cnt, l; int x;
		if (L <= mid && (x = locate(lc, l, mid, L, R)))  return push_up(p), x;
		if (R > mid && (x = locate(rc, mid + 1, r, L, R)))  return push_up(p), x; return push_up(p), 0;
	}
	#undef lc
	#undef rc
	#undef mid
} SGT;
void Tarjan(int u, int is_rt) {
	dfn[u] = low[u] = dfn_cnt; int ch = 0, v;
	while ((v = SGT.locate(rt[u], 1, n << 1, L[u], R[u]))) {
		ch ++ , Tarjan(v, 0), chkmin(low[u], low[v]);
		if ( ! is_rt && low[v] >= dfn[u])  ans[u] = 1;
	} chkmin(low[u], SGT.query(rt[u], 1, n << 1, L[u], R[u]));
	if (is_rt && ch >= 2)  ans[u] = 1;
} signed main() {
	ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
	cin >> n, SGT.tr[0].val = INF;
	_for (i, 1, n)  cin >> L[i] >> R[i], ins[L[i] += n].pb(i), del[(R[i] += n) + 1].pb(i);
	_for (i, n + 1, n << 1)  cin >> L[i] >> R[i], ins[L[i]].pb(i), del[R[i] + 1].pb(i);
	_for (i, 1, n << 1) {
		rt[i] = rt[i - 1];
		for (int j : ins[i])  SGT.update(rt[i], 1, n << 1, j, 1);
		for (int j : del[i])  SGT.update(rt[i], 1, n << 1, j, - 1);
	} _for (i, 1, n << 1)  if ( ! dfn[i])  SGT.tr[id[i]].cnt = 0, SGT.tr[id[i]].val = ++ dfn_cnt, Tarjan(i, 1);
	_for (i, 1, n)  cout << ans[i]; cout << "\n";
	_for (i, n + 1, n << 1)  cout << ans[i]; cout << "\n";
	return 0;
}
posted @ 2025-03-24 19:35  Ray_Wu  阅读(19)  评论(0)    收藏  举报