const int N = 1e5 + 5;
const int L = 16;
const int L2 = 13;
const int M = 6255;
int n, m;
struct RMQ
{
int a[N], bel[N], Log[N];
int bl[M], br[M], f[L2][M];
int pre[M][L], suf[M][L], pos[M][L];
int stk[L + 1], top;
inline void Init()
{
Log[0] = -1;
for (int i = 1; i <= n; ++i)
{
read(a[i]);
Log[i] = Log[i >> 1] + 1;
}
for (int i = 0; i <= bel[n]; ++i)
bl[i] = br[i] = 0;
for (int i = 1, t; i <= n; ++i)
{
t = bel[i] = (i - 1) / Log[n] + 1;
if (!bl[t])
bl[t] = i;
br[t] = i;
}
bl[n + 1] = br[n + 1] = bel[n] + 1;
for (int i = 1; i <= bel[n]; ++i)
{
pre[i][0] = a[bl[i]];
pos[i][0] = 1;
stk[top = 1] = bl[i];
for (int j = bl[i] + 1; j <= br[i]; ++j)
{
pre[i][j - bl[i]] = Max(pre[i][j - bl[i] - 1], a[j]);
int res = pos[i][j - bl[i] - 1];
while (top && a[stk[top]] <= a[j])
{
res ^= 1 << stk[top] - bl[i];
--top;
}
stk[++top] = j;
res |= 1 << j - bl[i];
pos[i][j - bl[i]] = res;
}
suf[i][br[i] - bl[i]] = a[br[i]];
for (int j = br[i] - 1; j >= bl[i]; --j)
suf[i][j - bl[i]] = Max(suf[i][j - bl[i] + 1], a[j]);
}
for (int i = 1; i <= bel[n]; ++i)
f[0][i] = pre[i][br[i] - bl[i]];
for (int j = 1; j <= Log[bel[n]]; ++j)
for (int i = 1; i + (1 << j) - 1 <= bel[n]; ++i)
f[j][i] = Max(f[j - 1][i], f[j - 1][i + (1 << j - 1)]);
}
inline int queryMax(int l, int r)
{
int tl = bel[l], tr = bel[r];
if (tl == tr)
{
int s = pos[tl][r - bl[tl]] >> l - bl[tl];
return a[Log[s & -s] + l];
}
int res = Max(suf[tl][l - bl[tl]], pre[tr][r - bl[tr]]);
if (++tl <= --tr)
{
int k = Log[tr - tl + 1];
CkMax(res, Max(f[k][tl], f[k][tr - (1 << k) + 1]));
}
return res;
}
}T;
unsigned long long
暂存计算结果,减少取模次数。struct line
{
ll k, b;
line() {}
line(ll K, ll B):
k(K), b(B) {}
inline ll ask(int x) const {return k * x + b;}
inline bool Under(const line &a, int l, int r) const
{
return ask(l) <= a.ask(l) && ask(r) <= a.ask(r);
}
};
int lc[M], rc[M];
ll tag[M];
line t[M];
inline void newNode(int &x)
{
x = ++T;
t[x] = line(0, Maxn);
}
inline void addTag(int x, ll v)
{
if (!x)
return ;
tag[x] += v;
t[x].b += v;
}
inline void pushDown(int x)
{
if (tag[x] != 0)
{
addTag(lc[x], tag[x]);
addTag(rc[x], tag[x]);
tag[x] = 0;
}
}
inline void addLine(int &x, const line &a, int l, int r)
{
if (!x)
newNode(x);
if (t[x].Under(a, l, r))
return ;
if (a.Under(t[x], l, r))
{
t[x] = a;
return ;
}
pushDown(x);
int mid = l + r >> 1;
addLine(lc[x], a, l, mid);
addLine(rc[x], a, mid + 1, r);
}
inline void Merge(int &x, int y, int l, int r)
{
if (!x || !y)
return (void)(x = x + y);
addLine(x, t[y], l, r);
if (l == r)
return ;
pushDown(x);
pushDown(y);
int mid = l + r >> 1;
Merge(lc[x], lc[y], l, mid);
Merge(rc[x], rc[y], mid + 1, r);
}
inline ll Query(int x, int l, int r, int v)
{
if (!x)
return Maxn;
ll res = t[x].ask(v);
if (l == r)
return res;
pushDown(x);
int mid = l + r >> 1;
CkMin(res, v <= mid ? Query(lc[x], l, mid, v) : Query(rc[x], mid + 1, r, v));
return res;
}
(
记作 1,)
记作 -1,记区间 s s s (保证长度为偶数)的前缀最小值为 p r e s pre_s pres,后缀最大值为 s u f s suf_s sufs,不难发现答案为 ⌈ p r e s 2 ⌉ + ⌈ s u f s 2 ⌉ \lceil \frac{pre_s}{2} \rceil + \lceil \frac{suf_s}{2} \rceil ⌈2pres⌉+⌈2sufs⌉。int len[N4], maxt[N4], mint[N4], addt[N4];
struct node
{
int maxv, minv, sl, sr, maxc, minc;
ll sum; /* from the top to the bottom: maxv - sr - sl - minv */
inline void check_seg(int a)
{
if (a > minv && a < maxv)
{
CkMin(sl, a);
CkMax(sr, a);
}
}
friend inline node operator + (const node &a, const node &b)
{
node c;
c.sum = a.sum + b.sum;
c.maxv = Max(a.maxv, b.maxv);
c.maxc = a.maxc * (a.maxv == c.maxv) + b.maxc * (b.maxv == c.maxv);
c.minv = Min(a.minv, b.minv);
c.minc = a.minc * (a.minv == c.minv) + b.minc * (b.minv == c.minv);
c.sl = Min(a.sl, b.sl);
c.sr = Max(a.sr, b.sr);
if (c.maxv != c.minv)
{
c.check_seg(a.maxv);
if (a.maxv != a.minv)
c.check_seg(a.minv);
c.check_seg(b.maxv);
if (b.maxv != b.minv)
c.check_seg(b.minv);
}
return c;
}
}tr[N4];
inline void addTag(int s, int v)
{
addt[s] += v;
tr[s].maxv += v;
tr[s].minv += v;
tr[s].sum += 1ll * v * len[s];
tr[s].sl != Maxn ? tr[s].sl += v : 0;
tr[s].sr != Minn ? tr[s].sr += v : 0;
maxt[s] != Minn ? maxt[s] += v : 0;
mint[s] != Maxn ? mint[s] += v : 0;
}
inline void maxTag(int s, int v)
{
if (tr[s].minv >= v || maxt[s] >= v)
return ;
tr[s].sum += 1ll * (v - tr[s].minv) * tr[s].minc;
if (v >= tr[s].maxv)
{
if (tr[s].maxv != tr[s].minv)
{
tr[s].sum += 1ll * (v - tr[s].maxv) * tr[s].maxc;
tr[s].maxc = tr[s].minc = len[s];
}
tr[s].maxv = v;
}
tr[s].minv = v;
CkMax(mint[s], v);
maxt[s] = v;
}
inline void minTag(int s, int v)
{
if (tr[s].maxv <= v || mint[s] <= v)
return ;
tr[s].sum += 1ll * (v - tr[s].maxv) * tr[s].maxc;
if (v <= tr[s].minv)
{
if (tr[s].maxv != tr[s].minv)
{
tr[s].sum += 1ll * (v - tr[s].minv) * tr[s].minc;
tr[s].maxc = tr[s].minc = len[s];
}
tr[s].minv = v;
}
tr[s].maxv = v;
CkMin(maxt[s], v);
mint[s] = v;
}
inline void pushDown(int s)
{
if (addt[s] != 0)
{
addTag(sL, addt[s]);
addTag(sR, addt[s]);
addt[s] = 0;
}
if (maxt[s] != Minn)
{
maxTag(sL, maxt[s]);
maxTag(sR, maxt[s]);
maxt[s] = Minn;
}
if (mint[s] != Maxn)
{
minTag(sL, mint[s]);
minTag(sR, mint[s]);
mint[s] = Maxn;
}
}
inline void modifyMax(int s, int l, int r, int x, int y, int v)
{
if (l == x && r == y && v < tr[s].sl)
return maxTag(s, v);
pushDown(s);
int mid = l + r >> 1;
if (y <= mid)
modifyMax(sL, l, mid, x, y, v);
else if (x > mid)
modifyMax(sR, mid + 1, r, x, y, v);
else
{
modifyMax(sL, l, mid, x, mid, v);
modifyMax(sR, mid + 1, r, mid + 1, y, v);
}
Update(s);
}
inline void modifyMin(int s, int l, int r, int x, int y, int v)
{
if (l == x && r == y && v > tr[s].sr)
return minTag(s, v);
pushDown(s);
int mid = l + r >> 1;
if (y <= mid)
modifyMin(sL, l, mid, x, y, v);
else if (x > mid)
modifyMin(sR, mid + 1, r, x, y, v);
else
{
modifyMin(sL, l, mid, x, mid, v);
modifyMin(sR, mid + 1, r, mid + 1, y, v);
}
Update(s);
}
inline void Merge(int &x, int y, int l, int r)
{
if (!x || !y)
{
x = x + y;
return ;
}
int mid = l + r >> 1;
Merge(lc(x), lc(y), l, mid);
Merge(rc(x), rc(y), mid + 1, r);
sze[x] += sze[y];
deleteNode(y);
}
inline void Split(int x, int l, int r, int &a, int &b, int k)
{
if (l == r)
{
a = x;
b = 0;
return ;
}
int mid = l + r >> 1;
if (k <= cnt[lc[x]])
{
a = newNode();
b = x;
Split(lc[x], l, mid, lc[a], lc[b], k);
}
else
{
a = x;
b = newNode();
Split(rc[x], mid + 1, r, rc[a], rc[b], k - cnt[lc[x]]);
}
Update(a);
Update(b);
}
set
或一般的线段树维护连续段的信息。set
维护连续段的部分有一定细节,为避免不必要的调试,这里提供一个模板。struct seg
{
int l, r;
bool rev; //是否为倒序
seg() {}
seg(int L, int R, bool Rev):
l(L), r(R), rev(Rev) {}
inline bool operator < (const seg &a) const
{
return l < a.l;
}
};
set<seg> s;
typedef set<seg>::iterator it;
inline void Cut(int x) //将 x 和 x 右侧的连续段切割开
{
it p = s.lower_bound(seg(x + 1, 0, false));
seg t = *--p;
if (t.r <= x)
return ;
s.erase(p);
/* remove information in t.l */
int a, b;
if (!t.rev)
{
Split(rt[t.l], 1, n, a, b, x - t.l + 1);
rt[t.l] = a;
rt[x + 1] = b;
/* add information in t.l */
/* add information in (x + 1) */
}
else
{
Split(rt[t.l], 1, n, a, b, t.r - x);
rt[t.l] = b;
rt[x + 1] = a;
/* add information in t.l */
/* add information in (x + 1) */
}
s.insert(seg(t.l, x, t.rev));
s.insert(seg(x + 1, t.r, t.rev));
}
inline void Reverse(int l, int r)
{
if (l > 1)
Cut(l - 1);
Cut(r);
vector<seg> cur;
it p = s.lower_bound(seg(l, 0, false));
cur.emplace_back(*p);
/* remove information in p->l */
for (++p; p != s.end() && p->r <= r; ++p)
{
/* remove information in p->l */
Merge(rt[l], rt[p->l], 1, n);
cur.emplace_back(*p);
}
for (seg x : cur)
s.erase(x);
s.insert(seg(l, r, rev));
/* add information in l */
}
inline void pushdownOut(int x, int y)
{
int _lc = lc(x) ? lc(x) : lc(y),
_rc = rc(x) ? rc(x) : rc(y);
mx(x) = Max(mx(_lc), mx(_rc));
}
inline void insertOut(int &x, int y, int l, int r, int u)
{
if (!x)
x = newNode();
if (l == r)
return (void)(mx(x) = cnt[l] - 1);
int mid = l + r >> 1;
u <= mid ? insertOut(lc(x), lc(y), l, mid, u) : insertOut(rc(x), rc(y), mid + 1, r, u);
pushdownOut(x, y);
}
inline void mergeOut(int &x, int y, int z, int l, int r)
{
if (!x || !y)
return (void)(x += y);
if (l == r)
return (void)(mx(x) += mx(y) - cnt[l], deleteNode(y));
int mid = l + r >> 1;
mergeOut(lc(x), lc(y), lc(z), l, mid);
mergeOut(rc(x), rc(y), rc(z), mid + 1, r);
pushdownOut(x, z);
deleteNode(y);
}
inline int queryOut(int x, int y, int l, int r, int v)
{
if (!x && !y)
return 0;
if (l == r)
return mx(x ? x : y) >= v ? l : 0;
int mid = l + r >> 1;
return mx(rc(x) ? rc(x) : rc(y)) >= v ?
queryOut(rc(x), rc(y), mid + 1, r, v) : queryOut(lc(x), lc(y), l, mid, v);
}
inline void dfs1(int x)
{
sze[x] = 1;
for (arc *e = adj[x]; e; e = e->nxt)
{
int y = e->to;
if (y == fa[x])
continue ;
fa[y] = x;
dep[y] = dep[x] + 1;
dfs1(y);
sze[x] += sze[y];
if (sze[y] > sze[son[x]])
son[x] = y;
}
}
inline void dfs2(int x)
{
if (son[x])
{
pos[son[x]] = ++V;
top[son[x]] = top[x];
idx[V] = son[x];
dfs2(son[x]);
}
int y;
for (arc *e = adj[x]; e; e = e->nxt)
if (!top[y = e->to])
{
pos[y] = ++V;
idx[V] = y;
top[y] = y;
dfs2(y);
}
}
inline void Init()
{
dfs1(1);
pos[1] = idx[1] = top[1] = V = 1;
dfs2(1);
}
inline int pathQuery(int x, int y)
{
int res = 0;
while (top[x] != top[y])
{
if (dep[top[x]] < dep[top[y]])
std::swap(x, y);
res += querySum(1, 1, n, pos[top[x]], pos[x]);
x = fa[top[x]];
}
if (dep[x] > dep[y])
std::swap(x, y);
return res + querySum(1, 1, n, pos[x], pos[y]);
}
实现时需注意 [ l , r ] [l,r] [l,r] 在同一块内的情况以及分块的边界问题。
区间众数。
将序列平均分成 n \sqrt n n 块,预处理 c n t [ i ] [ j ] cnt[i][j] cnt[i][j] 表示元素 i i i 在前 j j j 块中出现的次数, a n s [ i ] [ j ] ans[i][j] ans[i][j] 表示第 i i i 块到第 j j j 块的众数。
c n t [ i ] [ j ] cnt[i][j] cnt[i][j] 即先枚举 j j j 后枚举 i i i 统计, a n s [ i ] [ j ] ans[i][j] ans[i][j] 即先枚举 i i i,将第 i i i 块及其之后的数依次加入,用桶维护众数。
询问时设完整块为第 L L L 块到第 R R R 块,则答案要么为 a n s [ L ] [ R ] ans[L][R] ans[L][R],要么为非完整块中的数,暴力枚举即可。
时间复杂度 O ( n n ) \mathcal O(n \sqrt n) O(nn)。
插入删除元素,询问元素的最大值/最小值。
待补充。
允许离线,无修改,询问区间。
允许离线,带修改,询问区间。
将序列所有点分块,块的大小为 n 2 3 n^{\frac{2}{3}} n32,共有 n 1 3 n^{\frac{1}{3}} n31 个块,将询问按左端点所在块为第一关键字,右端点所在块为第二关键字,询问的时间为第三关键字进行排序,易分析出总时间复杂度为 O ( n 5 3 ) \mathcal O(n^{\frac{5}{3}}) O(n35)。
时间指针的移动即修改操作正向和逆向的进行。
for (int i = 1, l, r; i <= m; ++i)
{
char ch;
while (ch = getchar(), ch != 'R' && ch != 'Q');
if (ch == 'Q')
{
++qm;
q[qm].scan(pm, qm);
}
else
{
read(l); read(r);
p[++pm] = modify(l, _a[l], r);
_a[l] = r;
}
}
std::sort(q + 1, q + qm + 1);
int tl = 1, tr = 0, tt = 0;
for (int i = 1; i <= qm; ++i)
{
int l = q[i].l, r = q[i].r;
while (tt < q[i].t)
{
modify b = p[++tt];
if (b.x >= tl && b.x <= tr)
{
if (!--cnt[b.pre])
--ans;
if (!cnt[b.suf]++)
++ans;
}
a[b.x] = b.suf;
}
while (tt > q[i].t)
{
modify b = p[tt--];
if (b.x >= tl && b.x <= tr)
{
if (!--cnt[b.suf])
--ans;
if (!cnt[b.pre]++)
++ans;
}
a[b.x] = b.pre;
}
while (tl < l)
if (!--cnt[a[tl++]])
--ans;
while (tl > l)
if (!cnt[a[--tl]]++)
++ans;
while (tr > r)
if (!--cnt[a[tr--]])
--ans;
while (tr < r)
if (!cnt[a[++tr]]++)
++ans;
fans[q[i].id] = ans;
}
[ t l , x − 1 ] → [ t l , x ] ( t r < x ≤ r ) [tl,x - 1]\to[tl,x]\ (tr < x \le r) [tl,x−1]→[tl,x] (tr<x≤r)
记 F ( x , r ) = f ( x , 1 , r ) F(x,r) = f(x,1,r) F(x,r)=f(x,1,r),对答案产生的贡献为
f ( x , t l , x − 1 ) = F ( x , x − 1 ) − F ( x , t l − 1 ) f(x,tl,x - 1)=F(x,x - 1) - F(x, tl - 1) f(x,tl,x−1)=F(x,x−1)−F(x,tl−1)
其中 F ( x , x − 1 ) F(x,x-1) F(x,x−1) 很容易预处理, F ( x , t l − 1 ) F(x,tl - 1) F(x,tl−1) 则可以通过扫描线将询问区间 [ t r + 1 , r ] [tr + 1,r] [tr+1,r] 挂在 t l − 1 tl - 1 tl−1 处暴力询问得到。
如上所述,二次离线莫队即是把莫队移动的操作预处理以降低总的时间复杂度。
常见于询问区间内符合某种性质的点对数,莫队的单次移动可看一次询问(查询符合条件的点数)和一次修改(加入该点),设单次询问的复杂度为 O ( f ( n ) ) \mathcal O(f(n)) O(f(n)),单次修改的复杂度为 O ( g ( n ) ) \mathcal O(g(n)) O(g(n)) ,则上述做法使总时间复杂度由 O ( n n ( f ( n ) + g ( n ) ) ) \mathcal O(n\sqrt n(f(n) + g(n))) O(nn(f(n)+g(n))) 降至 O ( n g ( n ) + n n f ( n ) ) \mathcal O(ng(n) + n\sqrt n f(n)) O(ng(n)+nnf(n)),且空间复杂度依然为 O ( n ) \mathcal O(n) O(n)。
// 假定 F(x, x) = F(x, x - 1),sum 数组为 F(x, x - 1) 的前缀和
// 以下为扫描线预处理部分,注意 ans 数组存储的只是最终答案的差分形式
for (int i = 1; i <= m; ++i)
{
int l = q[i].l, r = q[i].r;
if (tr < r)
{
v[tl - 1].push_back(segQuery(tr + 1, r, -1, i));
ans[i] += sum[r] - sum[tr];
tr = r;
}
if (tr > r)
{
v[tl - 1].push_back(segQuery(r + 1, tr, 1, i));
ans[i] -= sum[tr] - sum[r];
tr = r;
}
if (tl < l)
{
v[tr].push_back(segQuery(tl, l - 1, -1, i));
ans[i] += sum[l - 1] - sum[tl - 1];
tl = l;
}
if (tl > l)
{
v[tr].push_back(segQuery(l, tl - 1, 1, i));
ans[i] -= sum[tl - 1] - sum[l - 1];
tl = l;
}
}
即 dsu on tree \text{dsu on tree} dsu on tree,解决的问题类型如下,常见的有子树数内外数颜色和求众数等:
先将所有询问挂在对应的子树根结点 x x x 上,考虑先遍历子结点 y y y 的子树处理其询问,除了最后一个子树外,每遍历完一棵子树就要清除它的影响,而最后一棵子树的信息则可以继承给 x x x。
我们令最后一棵子树为以重儿子为根的子树,由重链剖分的性质,从根到某结点的路径上的轻边个数为 O ( log n ) \mathcal O(\log n) O(logn),因此每个点被清除的次数为 O ( log n ) \mathcal O(\log n) O(logn)。
设计算单点贡献的时间复杂度为 O ( k ) \mathcal O(k) O(k),总时间复杂度 O ( k n log n ) \mathcal O(kn \log n) O(knlogn)。
预处理同重链剖分,具体算法流程如下:
inline void addSubtree(int x)
{
for (int i = pos[x], im = pos[x] + sze[x] - 1; i <= im; ++i)
addCol(col[idx[i]]);
}
inline void decSubtree(int x)
{
for (int i = pos[x], im = pos[x] + sze[x] - 1; i <= im; ++i)
decCol(col[idx[i]]);
}
inline void dfsTraverse(int x)
{
for (arc *e = adj[x]; e; e = e->nxt)
{
int y = e->to;
if (y == fa[x] || y == son[x])
continue ;
dfsTraverse(y);
decSubtree(y);
}
if (son[x])
dfsTraverse(son[x]);
addCol(col[x]);
for (arc *e = adj[x]; e; e = e->nxt)
{
int y = e->to;
if (y == fa[x] || y == son[x])
continue ;
addSubtree(y);
}
for (int i = 0, im = ask[x].size(); i < im; ++i)
{
pair<int, int> y = ask[x][i];
ans[y.second] = sum[y.first];
}
}
inline bool cmp(const int &x, const int &y)
{
return dfn[x] < dfn[y];
}
inline bool isSubtree(int x, int y)
{
return dfn[y] >= dfn[x] && dfn[y] <= dfn[x] + sze[x] - 1;
}
inline void auxTree()
{
top = 0;
std::sort(vir + 1, vir + m + 1, cmp);
for (int i = 1; i <= m; ++i)
key[vir[i]] = true;
for (int i = 1, im = m; i < im; ++i)
vir[++m] = queryLCA(vir[i], vir[i + 1]);
std::sort(vir + 1, vir + m + 1, cmp);
m = std::unique(vir + 1, vir + m + 1) - vir - 1;
for (int i = 1; i <= m; ++i)
{
while (top && !isSubtree(stk[top], vir[i]))
--top;
if (top)
par[vir[i]] = stk[top];
stk[++top] = vir[i];
}
}
inline void solve(int tl, int tr, int l, int r)
{
if (tl > tr)
return ;
if (l == r)
{
for (int i = tl; i <= tr; ++i)
if (k[cur[i]] == 2)
ans[cur[i]] = l;
return ;
}
int dl = 0, dr = 0, mid = l + r >> 1, tm;
++tis; // 对 BIT 做时间戳标记,免去清空
for (int i = tl; i <= tr; ++i)
if (k[cur[i]] == 1)
{
if (c[cur[i]] <= mid)
{
h1[++dl] = cur[i];
secModify(a[cur[i]], b[cur[i]]);
}
else
h2[++dr] = cur[i];
}
else
{
ll tmp = segQuery(a[cur[i]], b[cur[i]]);
if (c[cur[i]] > tmp)
{
h2[++dr] = cur[i];
c[cur[i]] -= tmp;
}
else
h1[++dl] = cur[i];
}
tm = tl + dl;
for (int i = tl; i < tm; ++i)
cur[i] = h1[i - tl + 1];
for (int i = tm; i <= tr; ++i)
cur[i] = h2[i - tm + 1];
solve(tl, tm - 1, l, mid);
solve(tm, tr, mid + 1, r);
}
Splay
至少一次使树的形态改变,防止被特殊构造的数据针对。const int N = 2e6 + 5;
int n, bm, m, T_splay, rt;
int a[N], b[N], c[N];
int rev[N], val[N], fa[N], lc[N], rc[N], sze[N], cnt[N];
inline void addRev(int x)
{
if (!x) return ;
rev[x] ^= 1;
std::swap(lc[x], rc[x]);
}
inline void pushDown(int x)
{
if (rev[x])
{
addRev(lc[x]);
addRev(rc[x]);
rev[x] = 0;
}
}
inline void Update(int x)
{
sze[x] = sze[lc[x]] + sze[rc[x]] + cnt[x];
}
inline void Rotate(int x)
{
int y = fa[x], z = fa[y];
pushDown(y);
pushDown(x);
bool flag = lc[y] == x;
int b = flag ? rc[x] : lc[x];
fa[x] = z, fa[y] = x;
b ? fa[b] = y : 0;
z ? (lc[z] == y ? lc[z] : rc[z]) = x : 0;
flag ? (rc[x] = y, lc[y] = b) : (lc[x] = y, rc[y] = b);
Update(y);
}
inline bool whichSide(int x)
{
return rc[fa[x]] == x;
}
inline void Splay(int x, int tar)
{
while (fa[x] != tar)
{
if (fa[fa[x]] != tar)
Rotate(whichSide(fa[x]) == whichSide(x) ? fa[x] : x);
Rotate(x);
}
Update(x);
!tar ? rt = x : 0;
}
inline void Insert(int v)
{
int x = rt, y = 0, dir;
while (x)
{
pushDown(x);
++sze[y = x];
if (val[x] == v)
{
++cnt[x];
Splay(x, 0);
return ;
}
if (v < val[x])
dir = 0, x = lc[x];
else
dir = 1, x = rc[x];
}
fa[x = ++T_splay] = y;
val[x] = v;
sze[x] = cnt[x] = 1;
y ? (dir ? rc[y] : lc[y]) = x : 0;
Splay(x, 0);
}
inline int Find(int v)
{
int x = rt;
while (x)
{
pushDown(x);
if (val[x] == v)
return Splay(x, 0), x;
x = v < val[x] ? lc[x] : rc[x];
}
return 0;
}
inline int getKth(int k)
{
int x = rt;
if (sze[x] < k)
return 0;
while (x)
{
pushDown(x);
if (k <= sze[lc[x]])
x = lc[x];
else
{
k -= sze[lc[x]] + cnt[x];
if (k <= 0)
return Splay(x, 0), x;
x = rc[x];
}
}
return 0;
}
inline int getRank(int v)
{
int x = rt, y, k = 1;
while (x)
{
pushDown(x);
y = x;
if (val[x] == v)
{
k += sze[lc[x]];
Splay(y, 0);
return k;
}
if (val[x] < v)
k += sze[lc[x]] + cnt[x], x = rc[x];
else
x = lc[x];
}
return Splay(y, 0), k;
}
inline int findPre(int v)
{
int x = rt, res = 0;
while (x)
{
pushDown(x);
if (val[x] < v)
res = x, x = rc[x];
else
x = lc[x];
}
Splay(res, 0);
return val[res];
}
inline int findSuf(int v)
{
int x = rt, res = 0;
while (x)
{
pushDown(x);
if (val[x] > v)
res = x, x = lc[x];
else
x = rc[x];
}
Splay(res, 0);
return val[res];
}
inline void Join(int x, int y)
{
int k = y;
pushDown(k);
while (lc[k])
{
k = lc[k];
pushDown(k);
}
lc[k] = x;
fa[x] = k;
fa[rt = y] = 0;
Splay(k, 0);
}
inline void Delete(int v)
{
int x = Find(v);
if (cnt[x] > 1)
{
--cnt[x];
--sze[x];
return ;
}
if (!lc[x] || !rc[x])
fa[rt = lc[x] + rc[x]] = 0;
else
Join(lc[x], rc[x]);
}
inline int Build(int _fa, int l, int r)
{ // 如是需要对原序列 a 按权值大小建树,数组 b 为排序去重后的结果,数组 c 为对应权值的种数
if (l > r)
return 0;
int mid = l + r >> 1, x = ++T_splay;
fa[x] = _fa;
val[x] = b[mid];
cnt[x] = c[mid];
lc[x] = Build(x, l, mid - 1);
rc[x] = Build(x, mid + 1, r);
return Update(x), x;
}
inline void Reverse(int l, int r)
{
int x = getKth(l),
y = getKth(r + 2);
Splay(x, 0);
Splay(y, x);
addRev(lc[y]);
}
inline void Print(int x)
{
if (!x)
return ;
pushDown(x);
Print(lc[x]);
if (val[x] != 0)
put(val[x]), putchar(' ');
Print(rc[x]);
}
Splay
操作与一般的 Splay \text{Splay} Splay 有所不同,一般的 Splay \text{Splay} Splay 在找到特定结点前都会经过根结点到该结点的路径,从而完成标记下传,但 LCT \text{LCT} LCT 一般都是直接指定某个结点进行操作,Splay
前需在对应的 Splay \text{Splay} Splay 中先完成从根结点到该节点的标记下传。const int N = 3e5 + 5;
int qr, que[N], val[N], rev[N], lc[N], rc[N], fa[N], sze[N];
inline bool whichSide(int x)
{
return lc[fa[x]] == x;
}
inline bool isRoot(int x)
{
return lc[fa[x]] != x && rc[fa[x]] != x;
}
inline void Update(int x)
{
sze[x] = sze[lc[x]] + sze[rc[x]] + 1;
}
inline void addRev(int x)
{
if (!x)
return ;
rev[x] ^= 1;
std::swap(lc[x], rc[x]);
}
inline void pushDown(int x)
{
if (rev[x])
{
addRev(lc[x]);
addRev(rc[x]);
rev[x] = 0;
}
}
inline void Rotate(int x)
{
int y = fa[x], z = fa[y];
bool flag = lc[y] == x;
int b = flag ? rc[x] : lc[x];
!isRoot(y) ? (lc[z] == y ? lc[z] : rc[z]) = x : 0;
fa[x] = z, fa[y] = x;
b ? fa[b] = y : 0;
flag ? (rc[x] = y, lc[y] = b) : (lc[x] = y, rc[y] = b);
Update(y);
}
inline void Splay(int x)
{
que[qr = 1] = x;
for (int y = x; !isRoot(y); y = fa[y])
que[++qr] = fa[y];
for (int i = qr; i >= 1; --i)
pushDown(que[i]);
while (!isRoot(x))
{
if (!isRoot(fa[x]))
Rotate(whichSide(fa[x]) == whichSide(x) ? fa[x] : x);
Rotate(x);
}
Update(x);
}
inline void Access(int x)
{
for (int y = 0; x; y = x, x = fa[x])
{
Splay(x);
rc[x] = y;
Update(x);
}
}
inline void makeRoot(int x)
{
Access(x);
Splay(x);
addRev(x);
}
inline int findRoot(int x)
{
Access(x);
Splay(x);
while (pushDown(x), lc[x])
x = lc[x];
return x;
}
inline void Link(int x, int y)
{
makeRoot(x);
fa[x] = y;
}
inline void Cut(int x, int y)
{
makeRoot(x);
Access(y);
Splay(y);
lc[y] = fa[x] = 0;
Update(y);
}
inline int Select(int x, int y)
{
makeRoot(x);
Access(y);
Splay(y);
return y;
}
Rotate
操作询问 f a fa fa 数组的正确性。inline bool isRoot(int x)
{
fa[x] = ufs_find(fa[x]);
return lc[fa[x]] != x && rc[fa[x]] != x;
}
inline void Access(int x)
{
for (int y = 0; x; y = x, x = ufs_find(fa[x]))
{
Splay(x);
rc[x] = y;
Update(x);
}
}
makeRoot(rt); Access(x);
后输出 v s z e [ x ] vsze[x] vsze[x] 即可,具体实现区别如下。inline void Update(int x)
{
sze[x] = sze[lc[x]] + sze[rc[x]] + vsze[x] + 1;
}
inline void Access(int x)
{
for (int y = 0; x; y = x, x = fa[x])
{
Splay(x);
vsze[x] += sze[rc[x]];
rc[x] = y;
vsze[x] -= sze[rc[x]];
Update(x);
}
}
inline void Link(int x, int y)
{
makeRoot(x);
Access(y);
Splay(y);
fa[x] = y;
vsze[y] += sze[x];
Update(y);
}
nth_element
可能会使左右子树中均有与当前结点在当前维度坐标相同的结点,因此重复结点需要分开存储,且需要采用类似查询 K K K 维矩体的写法,一次查询会找到所有重复点。// 含类似替罪羊树实现的动态插入
const int K = 2;
const double alpha = 0.6;
int top, op, T, rt, m; ll sum[N], val[N];
int stk[N], erav[N], sze[N], cur[N], lc[N], rc[N];
bool del[N];
struct point
{
int a[K];
point() {}
point(int v)
{
for (int i = 0; i < K; ++i)
a[i] = v;
}
inline void scan()
{
for (int i = 0; i < K; ++i)
read(a[i]);
}
inline bool operator == (const point &x) const
{
for (int i = 0; i < K; ++i)
if (a[i] != x.a[i])
return false;
return true;
}
}p[N], erap[N];
const point minPoint = point(Minn);
const point maxPoint = point(Maxn);
struct rect
{
point tl, tr;
rect() {}
rect(point Tl, point Tr):
tl(Tl), tr(Tr) {}
inline void updatePoint(const point &x)
{
for (int i = 0; i < K; ++i)
CkMin(tl.a[i], x.a[i]);
for (int i = 0; i < K; ++i)
CkMax(tr.a[i], x.a[i]);
}
inline void updateRect(const rect &x)
{
for (int i = 0; i < K; ++i)
CkMin(tl.a[i], x.tl.a[i]);
for (int i = 0; i < K; ++i)
CkMax(tr.a[i], x.tr.a[i]);
}
inline bool inPoint(const point &x) const
{ // whether point x is in the rectangle
for (int i = 0; i < K; ++i)
if (x.a[i] < tl.a[i] || x.a[i] > tr.a[i])
return false;
return true;
}
inline bool intersectRect(const rect &x) const
{ // whether rectangle x intersects with the rectangle
for (int i = 0; i < K; ++i)
if (x.tr.a[i] < tl.a[i] || x.tl.a[i] > tr.a[i])
return false;
return true;
}
inline bool inRect(const rect &x) const
{ // whether rectangle x is in the rectangle
for (int i = 0; i < K; ++i)
if (x.tl.a[i] < tl.a[i] || x.tr.a[i] > tr.a[i])
return false;
return true;
}
}tr[N];
const rect nullRect = rect(maxPoint, minPoint);
inline bool cmp(const int &x, const int &y)
{
return erap[x].a[op] < erap[y].a[op];
}
inline void Update(int x)
{
sze[x] = 1;
if (del[x])
{
tr[x] = nullRect;
sum[x] = 0;
}
else
{
tr[x] = rect(p[x], p[x]);
sum[x] = val[x];
}
if (lc[x])
{
sze[x] += sze[lc[x]];
sum[x] += sum[lc[x]];
tr[x].updateRect(tr[lc[x]]);
}
if (rc[x])
{
sze[x] += sze[rc[x]];
sum[x] += sum[rc[x]];
tr[x].updateRect(tr[rc[x]]);
}
}
inline ll querySum(int x, const rect &u)
{
if (!x || !tr[x].intersectRect(u))
return 0;
if (u.inRect(tr[x]))
return sum[x];
ll res = 0;
if (u.inPoint(p[x]))
res += val[x];
res += querySum(lc[x], u);
res += querySum(rc[x], u);
return res;
}
inline int newNode(ll v, const point &u)
{
int x = top ? stk[top--] : ++T;
lc[x] = rc[x] = 0;
tr[x] = rect(u, u);
p[x] = u;
sze[x] = 1;
val[x] = sum[x] = v;
del[x] = false;
return x;
}
inline void Build(int &x, int l, int r, int _op)
{ // 若只需静态建树,请预先将结点信息存入 erap 和 erav 中
if (l > r)
return (void)(x = 0);
op = _op;
int mid = l + r >> 1;
std::nth_element(cur + l, cur + mid, cur + r + 1, cmp);
x = newNode(erav[cur[mid]], erap[cur[mid]]);
int nxt_op = _op + 1 == K ? 0 : _op + 1;
Build(lc[x], l, mid - 1, nxt_op);
Build(rc[x], mid + 1, r, nxt_op);
Update(x);
}
inline bool inBalanced(int x)
{
return sze[x] * alpha < Max(sze[lc[x]], sze[rc[x]]);
}
inline void delNode(int x)
{
lc[x] = rc[x] = sze[x] = sum[x] = val[x] = 0;
tr[x] = nullRect;
stk[++top] = x;
return ;
}
inline void Erase(int x)
{
if (!x)
return ;
Erase(lc[x]);
Erase(rc[x]);
++m;
erap[m] = p[x];
erav[m] = val[x];
delNode(x);
}
inline void reBuild(int &x, int _op)
{
m = 0;
Erase(x);
for (int i = 1; i <= m; ++i)
cur[i] = i;
Build(x, 1, m, _op);
}
inline void Insert(int &x, const point &u, ll v, int _op)
{
if (!x)
return (void)(x = newNode(v, u));
int nxt_op = _op + 1 == K ? 0 : _op + 1;
u.a[_op] < p[x].a[_op] ? Insert(lc[x], u, v, nxt_op) : Insert(rc[x], u, v, nxt_op);
Update(x);
int _sze = sze[x];
if (inBalanced(x))
reBuild(x, _op);
assert(_sze == sze[x]);
}
inline void Delete(int x, const rect &u)
{ // 将以结点 x 为根的子树中所有被 K 维矩体 u 包含的结点删除
if (!x || !tr[x].intersectRect(u))
return ;
if (u.inPoint(p[x]))
del[x] = true;
Delete(lc[x], u);
Delete(rc[x], u);
Update(x);
}
const int S = 1e3;
const int S2 = 2e3;
int tot_len;
struct node
{
node *nxt;
int sze, tag;
int b[S2 + 5];
node()
{
sze = 0;
nxt = NULL;
}
inline void Push(int c)
{
b[sze++] = c;
}
}*head = NULL;
inline void checkLarge(node *p)
{
if (p->sze >= S2)
{
node *q = new node;
for (int i = S; i < p->sze; ++i)
q->Push(p->b[i]);
p->sze = S;
q->nxt = p->nxt;
p->nxt = q;
}
}
inline void checkSmall(node *p)
{
if (p->nxt && p->sze + p->nxt->sze <= S)
{
node *q = p->nxt;
for (int i = 0; i < q->sze; ++i)
p->Push(q->b[i]);
p->nxt = q->nxt;
return ;
}
}
inline void Insert(int c, int pos)
{
node *p = head;
int cur, cnt;
if (pos >= ++tot_len)
{
while (p->nxt != NULL)
p = p->nxt;
p->Push(c);
checkLarge(p);
return ;
}
for (cur = head->sze; p != NULL && cur < pos; p = p->nxt, cur += p->sze);
cur -= p->sze;
cnt = pos - cur - 1;
for (int i = p->sze - 1; i >= cnt; --i)
p->b[i + 1] = p->b[i];
p->b[cnt] = c;
++p->sze;
checkLarge(p);
}
inline void find(node* &p, int &cnt, int pos)
{
p = head;
int cur;
for (cur = head->sze; p != NULL && cur < pos; p = p->nxt, cur += p->sze);
cur -= p->sze;
cnt = pos - cur - 1;
}
inline void find2(node* &p, node* &pre, int &cnt, int pos)
{
pre = NULL, p = head;
int cur;
for (cur = head->sze; p != NULL && cur < pos; pre = p, p = p->nxt, cur += p->sze);
cur -= p->sze;
cnt = pos - cur - 1;
}
inline void Delete(int l, int r)
{
node *p, *q, *pre;
int nl, nr;
find2(p, pre, nl, l);
find(q, nr, r);
if (p == q)
{
int len = nr - nl + 1;
for (int i = nr + 1; i < p->sze; ++i)
p->b[i - len] = p->b[i];
p->sze -= len;
if (!p->sze)
pre != NULL ? pre->nxt = p->nxt : head = p->nxt;
else
checkSmall(p);
return ;
}
p->sze = nl;
int len = nr + 1;
for (int i = nr + 1; i < q->sze; ++i)
q->b[i - len] = q->b[i];
q->sze -= len;
if (q->sze > 0)
p->nxt = q;
else
p->nxt = q->nxt;
if (p->sze > 0)
checkSmall(p);
else
pre != NULL ? pre->nxt = p->nxt : head = p->nxt;
}
inline int Query(int pos)
{
node *p = head;
int cur;
for (cur = head->sze; p != NULL && cur < pos; p = p->nxt, cur += p->sze);
cur -= p->sze;
return p->b[pos - cur - 1];
}