Paper Grading
题意:给定 n n n个字符串,有两种操作:
一、给定 i , j i, j i,j,交换第 i i i个跟第 j j j个字符串。
二、给定 str , k , l , r k, l, r k,l,r,问你在区间 [ l , r ] [l, r] [l,r]中的字符,与 str 至少有 k k k个公共前缀的字符串有多少个。
先建立一颗字典树,对于满足要求的字符串,我们可以在字典树中查找 str 的第 k k k 个字符落在的点 r t rt rt,
然后在以 r t rt rt 为根的子树上查找在区间 [ l , r ] [l, r] [l,r]的字符有多少个,
考虑在字典树 d f s dfs dfs序上,对每个节点建立一颗主席树,然后插入值,对于查询操作,我们只要查找两颗主席树上 [ l , r ] [l, r] [l,r]区间的和即可。
但是因为有带修操作,所以考虑树状数组套主席树,这样即可满足带修跟查找操作,发现一直 w a 7 wa\ 7 wa 7,(据我判断应该是内存不够)。
所以考虑如何优化,对于字典树来说,总长度可以说是固定的 2 × 1 0 5 2 \times 10 ^ 5 2×105,但是对于 n n n来说却是一般小于 2 × 1 0 5 2 \times 10 ^5 2×105的,对于上述的建树方式,
我们建立的主席树可以说也是固定的 2 × 1 0 5 2 \times 10 ^ 5 2×105棵,所以说整体来说是 2 × 1 0 5 log n log n 2 \times 10 ^ 5 \log n \log n 2×105lognlogn,
如果对每个字符串建立一颗主席树整体来说应该是 n log 2 × 1 0 5 log 2 × 1 0 5 n \log {2 \times 10 ^ 5} \log {2 \times 10 ^ 5} nlog2×105log2×105,在多数情况下应该是小于上着的。
所以对于查询我们只要对 [ l , r ] [l, r] [l,r]区间,查询 d f s dfs dfs序在 l [ r t ] , r [ r t ] l[rt], r[rt] l[rt],r[rt]之间的即可,对于修改操作我们交换 i , j i, j i,j位置的 d f s dfs dfs序即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int trie[N][26], cnt = 1;
int pos[N], value[N], l[N], r[N], tot, n, m;
int root[N], ls[N << 7], rs[N << 7], sum[N << 7], num;
int insert(string &str) {
int n = str.size(), rt = 1;
for (int i = 0; i < n; i++) {
if (!trie[rt][str[i] - 'a']) {
trie[rt][str[i] - 'a'] = ++cnt;
}
rt = trie[rt][str[i] - 'a'];
}
return rt;
}
void dfs(int rt) {
l[rt] = ++tot;
for (int i = 0; i < 26; i++) {
if (trie[rt][i]) {
dfs(trie[rt][i]);
}
}
r[rt] = tot;
}
void update(int &rt, int l, int r, int x, int v) {
if (!rt) {
rt = ++num;
}
sum[rt] += v;
if (l == r) {
return ;
}
int mid = l + r >> 1;
if (x <= mid) {
update(ls[rt], l, mid, x, v);
}
else {
update(rs[rt], mid + 1, r, x, v);
}
}
int A[50], B[50], cnt1, cnt2;
int query_sum(int l, int r, int L, int R) {
if (l >= L && r <= R) {
int ans = 0;
for (int i = 1; i <= cnt1; i++) {
ans -= sum[A[i]];
}
for (int i = 1; i <= cnt2; i++) {
ans += sum[B[i]];
}
return ans;
}
int mid = l + r >> 1, ans = 0, A1[50], B1[50];
if (L <= mid) {
for (int i = 1; i <= cnt1; i++) {
A1[i] = A[i];
A[i] = ls[A[i]];
}
for (int i = 1; i <= cnt2; i++) {
B1[i] = B[i];
B[i] = ls[B[i]];
}
ans += query_sum(l, mid, L, R);
for (int i = 1; i <= cnt1; i++) {
A[i] = A1[i];
}
for (int i = 1; i <= cnt2; i++) {
B[i] = B1[i];
}
}
if (R > mid) {
for (int i = 1; i <= cnt1; i++) {
A1[i] = A[i];
A[i] = rs[A[i]];
}
for (int i = 1; i <= cnt2; i++) {
B1[i] = B[i];
B[i] = rs[B[i]];
}
ans += query_sum(mid + 1, r, L, R);
for (int i = 1; i <= cnt1; i++) {
A[i] = A1[i];
}
for (int i = 1; i <= cnt2; i++) {
B[i] = B1[i];
}
}
return ans;
}
inline int lowbit(int x) {
return x & (-x);
}
void add(int pos, int x, int v) {
for (int i = pos; i <= n; i += lowbit(i)) {
update(root[i], 1, tot, x, v);
}
}
int get_sum(int l, int r, int L, int R) {
if (l > r || L > R) {
return 0;
}
cnt1 = cnt2 = 0;
for (int i = l - 1; i; i -= lowbit(i)) {
A[++cnt1] = root[i];
}
for (int i = r; i; i -= lowbit(i)) {
B[++cnt2] = root[i];
}
return query_sum(1, tot, L, R);
}
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
// ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> m;
string str;
for (int i = 1; i <= n; i++) {
cin >> str;
pos[i] = insert(str);
}
dfs(1);
for (int i = 1; i <= n; i++) {
pos[i] = l[pos[i]];
value[i] = i;
add(value[i], pos[i], 1);
}
for (int i = 1, op, k, a, b; i <= m; i++) {
cin >> op;
if (op == 1) {
cin >> a >> b;
add(value[a], pos[a], -1), add(value[b], pos[b], -1);
swap(pos[a], pos[b]);
add(value[a], pos[a], 1), add(value[b], pos[b], 1);
}
else {
cin >> str >> k >> a >> b;
int rt = 1;
for (int j = 0; j < k; j++) {
if (!trie[rt][str[j] - 'a']) {
rt = -1;
break;
}
rt = trie[rt][str[j] - 'a'];
}
if (rt == -1) {
cout << "0\n";
}
else {
cout << get_sum(a, b, l[rt], r[rt]) << "\n";
}
}
}
return 0;
}