[THUSC2016]补退选 - Trie
Description
维护一个字符串集合,有三种事件,加入一个字符串,删除一个字符串,询问最早在哪个事件之后,以某个串为前缀的字符串数量超过\(k\),强制在线。\(n \le 100000,|S| \le 60\),输入中的所有字符串只会包含前\(10\)个小写字母。
Solution
建立\(Trie\)树维护字符串集合,在\(Trie\)树上的每一个节点开一个\(vector\),用来记录子树内的结束节点到达某个值的最早时刻,查询时直接找到前缀对应节点即可。
Code
#include <bits/stdc++.h>
using namespace std;
const int _ = 1e6 + 10;
int N, tim;
inline int ty() {
char ch = getchar();
int x = 0, f = 1;
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
inline int getstr(char *s) {
char ch = getchar();
int cnt = 0;
while (ch >= 'a' && ch <= 'j') {
s[++cnt] = ch;
ch = getchar();
}
return cnt;
}
struct Trie {
int trie[_][10], cnt[_], tot;
vector<int> num[_];
void insert(char *s, int len) {
int x = 0;
for (int i = 1; i <= len; ++i) {
int ch = s[i] - 'a';
if (!trie[x][ch]) trie[x][ch] = ++tot;
x = trie[x][ch];
++cnt[x];
if (num[x].size() < cnt[x]) num[x].push_back(tim);
}
}
void erase(char *s, int len) {
int x = 0;
for (int i = 1; i <= len; ++i) {
int ch = s[i] - 'a';
x = trie[x][ch];
--cnt[x];
}
}
int query(char *s, int k, int len) {
int x = 0;
for (int i = 1; i <= len; ++i) {
int ch = s[i] - 'a';
if (!trie[x][ch]) return -1;
x = trie[x][ch];
}
if (num[x].size() <= k)
return -1;
else
return num[x][k];
}
} tr;
int main() {
#ifndef ONLINE_JUDGE
freopen("select.in", "r", stdin);
freopen("select.out", "w", stdout);
#endif
N = ty();
int last = 0;
for (tim = 1; tim <= N; ++tim) {
int op = ty();
char s[65];
int len = getstr(s);
if (op == 1)
tr.insert(s, len);
else if (op == 2)
tr.erase(s, len);
else if (op == 3) {
int a = ty(), b = ty(), c = ty();
int k = (1ll * a * abs(last) + b) % c;
printf("%d\n", last = tr.query(s, k, len));
}
}
return 0;
}
既然选择了远方,便只顾风雨兼程。