数据结构
莫队
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)\),则有:
每个都是普通莫队。
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;
}