2
考虑这样一个问题:在字符串的前面插入字符,动态查询后缀排序的结果。
考虑用平衡树维护。那么我们需要 \(O(1)\) 地比较两个后缀 \(s[i\dots n]\) 和 \(s[j\dots n]\) 的大小关系。
首先如果 \(s_i\not =s_j\) 就结束了。否则只需比较 \(s[i+1\dots n]\) 和 \(s[j+1\dots n]\) 即可。这两个后缀都已经被插入平衡树了。
考虑给平衡树上的每个节点都赋两个权值 \((l,r)\),一个节点的左右儿子的两个权值分别是 \((l,\frac{l+r}{2})\) 和 \((\frac{l+r}{2},r)\)。那么树上两个节点的大小关系就是 \(l+r\) 的大小关系。于是可以 \(O(1)\) 比较了。
注意到 \((l,r)\) 的信息是自顶向下转移的。所以我们使用的平衡树既不能是基于旋转的,也不能是基于分裂的。使用 \(\frac{1}{sz}\) 平衡树,即对于在插入/删除过程中经过的每一个节点,都以其 \(\frac{1}{sz}\) 的概率将其整棵子树重构。可以证明这样做的树高是 \(\log\) 的。
由于树高是期望 \(\log\) 而非严格 \(\log\),所以存权值不能用 long long 类型,要用 double。
模板题代码,但是被卡常
#include<bits/stdc++.h>
using namespace std;
const int maxn = 800005;
int n, m, msk, res;
string s;
stack<int> st;
int pre[maxn];
mt19937 rnd(time(0));
string decode(string s, int msk) {
for (int i = 0; i < s.size(); i++) {
msk = (msk * 131 + i) % s.size();
swap(s[i], s[msk]);
}
return s;
}
struct HeigeTree {
int sz[maxn], p[maxn], son[maxn][2], rt, cnt;
double x[maxn], y[maxn];
vector<int> tmp;
void pushup(int k) {sz[k] = sz[son[k][0]] + sz[son[k][1]] + p[k];}
int new_node(double l, double r, int id) {sz[id] = p[id] = 1; x[id] = l, y[id] = r; return id;}
bool cmp(int a, int b) {
if (s[a] < s[b]) return 1;
if (s[a] > s[b]) return 0;
return x[pre[a]] + y[pre[a]] < x[pre[b]] + y[pre[b]];
}
bool cmp(int p, string t) {
for (int i = 0; i < t.size(); i++) {
if (!p || s[p] < t[i]) return 1;
if (s[p] > t[i]) return 0;
p = pre[p];
}
return 0;
}
void dfs(int k) {
if (!k) return;
dfs(son[k][0]);
tmp.push_back(k);
dfs(son[k][1]);
}
int build(int l, int r, double L, double R) {
if (l > r) return 0;
int mid = l + r >> 1; int k = tmp[mid]; x[k] = L, y[k] = R;
son[k][0] = build(l, mid - 1, L, (L + R) / 2); son[k][1] = build(mid + 1, r, (L + R) / 2, R);
pushup(k);
return k;
}
int rebuild(int k, double l, double r) {tmp.clear(); dfs(k); return build(0, (int)tmp.size() - 1, l, r);}
int ins(int k, int v, double l, double r) {
if (!k) return new_node(l, r, v);
if (cmp(v, k)) son[k][0] = ins(son[k][0], v, l, (l + r) / 2);
else son[k][1] = ins(son[k][1], v, (l + r) / 2, r);
pushup(k);
if (rnd() % (sz[k] + 1) == 0) return rebuild(k, x[k], y[k]);
return k;
}
int del(int k, int v) {
sz[k]--;
if (k == v) {p[k] = 0; return k;}
if (x[v] + y[v] < x[k] + y[k]) son[k][0] = del(son[k][0], v);
else son[k][1] = del(son[k][1], v);
if (rnd() % (sz[k] + 1) == 0) return rebuild(k, x[k], y[k]);
return k;
}
int qry(int k, string s) {
if (!k) return 0;
if (cmp(k, s)) return qry(son[k][1], s) + sz[son[k][0]] + p[k];
else return qry(son[k][0], s);
}
int query(string s) {
int sum = qry(rt, s);
s.back()++;
return qry(rt, s) - sum;
}
} T;
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> s; s = " " + s;
st.push(0);
for (int i = 1; i < s.size(); i++) {
pre[i] = i - 1; st.push(i);
T.rt = T.ins(T.rt, i, 0, 1e9);
assert(T.p[i]);
}
m = s.size() - 1;
for (int i = 1; i <= n; i++) {
string p; cin >> p;
if (p == "QUERY") {
string s; cin >> s; s = decode(s, msk); reverse(s.begin(), s.end());
cout << (res = T.query(s)) << '\n';
msk ^= res;
}
else if (p == "ADD") {
string t; cin >> t; t = decode(t, msk);
for (int j = 0; j < t.size(); j++) {
s += t[j]; pre[++m] = st.top(); st.push(m);
T.rt = T.ins(T.rt, m, 0, 1e9);
}
}
else {
assert(p == "DEL");
int x; cin >> x;
for (int j = 0; j < x; j++) {
T.rt = T.del(T.rt, st.top());
st.pop();
}
}
}
return 0;
}

浙公网安备 33010602011771号