P8203 [传智杯 #4 决赛] DDOSvoid 的馈赠 solution
P8203 [传智杯 #4 决赛] DDOSvoid 的馈赠
纪念第一道模拟赛场切的黑。
首先离线,把询问 \((x, y)\) 的询问挂在更长的字符串上,假设为 \(y\)。然后建出 \(s\) 的 ACAM,并枚举 \(y\) 求答案。对于每一个 \(y\),我们根号分治,设块长为 \(B = \sqrt n\)。
对于 \(len_y > B\) 那么其最多只有 \(\sqrt n\) 个,我们对 fail 树上每个点预处理出每个节点的答案,具体方法就是拉出 \(y\) 经过 AC 自动机上的点在 fail 树上的虚树,如果点 \(i\) 在一条链上,其答案就是结尾在点 \(i\) 的 \(s\) 中字符串个数简记为 \(siz_i\),否则为 \(0\),并记录出从根到点的答案的前缀和。为了保证时间复杂度正确我们应预处理出每个字符串的虚树每个点按 dfn 序排列好的序列。然后我们枚举 \(x\),考虑计算其答案,枚举 \(x\) 虚树上的链,设链头为 \(u\) 链尾为 \(v\),链的贡献就应为 \(sum_v - sum_u\),\(x\) 的答案就是每个链的贡献之和。
对于 \(len_y < B\) 说明 \(len_x < B\) 也成立。我们发现答案就是虚树交集的所有点的 \(siz_i\) 之和,容斥成两个虚树分别的 \(siz_i\) 和减去并集的 \(siz_i\) 和。对于两个虚树分别的和我们可以直接暴力前缀和差分算因为其虚树节点个数是 \(\sqrt n\) 级别的。对于并,其虚树点集按照 dfn 序排列的序列可以通过两个数树的序列归并得到,搭配欧拉序 st 表可以线性的求出,算出点集后直接算即可。
做完了,时间复杂度与 \(O(n \sqrt n)\) 同阶。
code
#include <bits/stdc++.h>
using namespace std;
#define int int
#define ll long long
#define usd unsigned
#define el putchar('\n')
#define lowbit(x) (x & (-x))
#define AC return
#define AK return 0
#define YS cout << "YES"
#define NO cout << "NO"
#define Ys cout << "Yes"
#define No cout << "No"
#define ys cout << "yes"
#define no cout << "no"
#define ls(i) ch[i][0]
#define rs(i) ch[i][1]
#define debug(num) cerr << #num << ' ' << num << '\n'
#define void inline void
#define il inline
#define Mod(x) (((x) % mod + mod) % mod)
#define pii pair <int, int>
#define pll pair <ll, ll>
il char gc() { char c; while((c = getchar()) <= ' ') ; return c; }
il int rd() {
char c; int x, f = 1;
while(!isdigit(c = getchar())) if(c == '-') f *= -1;
x = c ^ 48;
while(isdigit(c = getchar())) x = (x << 3) + (x << 1) + (c ^ 48);
return x * f;
}
void read(string &s) {
char c; s.clear();
while((c = getchar()) <= ' ') ;
s = c;
while((c = getchar()) > ' ') s.push_back(c);
}
void ACehomoxue();
signed main() {
srand(time(0));
// freopen("sample_B4.in", "r", stdin);
// freopen(".out", "w", stdout);
int t = 1;
// t = rd();
while(t--) ACehomoxue();
AK;
}
const int mod = 998244353, maxn = 3 * 1e5 + 18, maxk = 21, block = 800;
int n, m, q, siz[maxn], sum[maxn], pre[maxn];
map <pii, int> mem;
vector <int> vt[maxn], pa[maxn], vec[maxn], st;
queue <int> qu;
int fail[maxn], ch[maxn][26], tot = 0;
string s;
void insert() {
int p = 0;
for(char c : s) {
int i = c - 'a';
if(!ch[p][i]) ch[p][i] = ++tot;
p = ch[p][i];
}
siz[p]++, pre[p]++;
}
int nxt[maxn * 2][maxk], cnt = 0, Log[maxn * 2], dep[maxn], in[maxn], dfn, posi[maxn];
#define minn(x, y) dep[x] < dep[y] ? x : y
void dfs(int x, int fa) {
posi[x] = cnt;
dep[x] = dep[fa] + 1, in[x] = ++dfn;
for(int to : vec[x]) {
nxt[++cnt][0] = to;
dfs(to, x);
nxt[++cnt][0] = x;
}
}
il int lca(int u, int v) {
u = posi[u], v = posi[v];
if(u > v) swap(u, v);
int k = Log[v - u + 1];
return minn(nxt[u][k], nxt[v - (1 << k) + 1][k]);
}
void acam() {
for(int i = 0; i < 26; i++) if(ch[0][i]) qu.push(ch[0][i]);
while(!qu.empty()) {
int x = qu.front(); qu.pop();
vec[fail[x]].push_back(x);
pre[x] += pre[fail[x]];
for(int i = 0; i < 26; i++) {
if(ch[x][i]) {
fail[ch[x][i]] = ch[fail[x]][i];
qu.push(ch[x][i]);
} else ch[x][i] = ch[fail[x]][i];
}
}
dfs(0, 0);
}
il bool cmp(const int &a, const int &b) { return in[a] < in[b]; }
bool vis[maxn];
void get(int id) {
int p = 0;
vt[id].push_back(p);
vis[p] = true;
for(char c : s) {
p = ch[p][c - 'a'];
if(!vis[p]) vt[id].push_back(p), vis[p] = true;
}
sort(vt[id].begin(), vt[id].end(), cmp);
for(int i = 1, sz = vt[id].size(); i < sz; i++) {
int pa = lca(vt[id][i - 1], vt[id][i]);
if(!vis[pa]) vis[pa] = true, vt[id].push_back(pa);
}
sort(vt[id].begin(), vt[id].end(), cmp);
pa[id].push_back(0);
for(int i = 1, sz = vt[id].size(); i < sz; i++) pa[id].push_back(lca(vt[id][i - 1], vt[id][i]));
for(int x : vt[id]) vis[x] = false;
}
vector <pii> ask[maxn];
int ans[maxn], val[maxn];
bool tag[maxn];
void lazytag(int x) {
for(int to : vec[x]) {
lazytag(to);
tag[x] = (tag[x] || tag[to]);
}
if(tag[x]) val[x] = siz[x];
else val[x] = 0;
}
void dfssum(int x) {
sum[x] += val[x];
for(int to : vec[x]) {
sum[to] += sum[x];
dfssum(to);
}
}
void merge(vector <int> &ans, vector <int> &v1, vector <int> &v2) {
int sz1 = v1.size(), sz2 = v2.size(), i1 = 0, i2 = 0; ans.clear();
while(i1 < sz1 && i2 < sz2) {
if(v1[i1] == v2[i2]) { ans.push_back(v1[i1]); i1++, i2++; continue; }
in[v1[i1]] < in[v2[i2]] ? ans.push_back(v1[i1++]) : ans.push_back(v2[i2++]);
}
while(i1 < sz1) ans.push_back(v1[i1++]);
while(i2 < sz2) ans.push_back(v2[i2++]);
}
vector <int> prt, stt;
void ACehomoxue() {
n = rd(), m = rd(), q = rd();
Log[0] = -1;
for(int i = 1; i < maxn * 2; i++) Log[i] = Log[i / 2] + 1;
for(int i = 1; i <= n; i++) {
read(s);
insert();
}
acam();
for(int k = 1; k < maxk; k++) {
for(int i = 1; i + (1 << k) <= cnt + 1; i++) {
nxt[i][k] = minn(nxt[i][k - 1], nxt[i + (1 << k - 1)][k - 1]);
}
}
for(int i = 1; i <= m; i++) {
read(s);
get(i);
}
for(int i = 1; i <= q; i++) {
int x = rd(), y = rd();
if(vt[x].size() > vt[y].size()) swap(x, y);
ask[y].push_back({x, i});
}
for(int i = 1; i <= m; i++) {
if(vt[i].size() > block) {
for(int x : vt[i]) tag[x] = true;
lazytag(0);
dfssum(0);
for(auto [j, id] : ask[i]) {
if(mem.count({i, j})) { ans[id] = mem[{i, j}]; continue; }
for(int k = 1, sz = vt[j].size(); k < sz; k++) {
ans[id] += sum[vt[j][k]] - sum[pa[j][k]];
}
mem[{i, j}] = ans[id];
}
for(int i = 0; i <= tot; i++) val[i] = sum[i] = tag[i] = 0;
} else {
int res = 0;
for(int k = 1, sz = vt[i].size(); k < sz; k++) res += pre[vt[i][k]] - pre[pa[i][k]];
for(auto [j, id] : ask[i]) {
if(mem.count({i, j})) { ans[id] = mem[{i, j}]; continue; }
ans[id] = res;
for(int k = 1, sz = vt[j].size(); k < sz; k++) ans[id] += pre[vt[j][k]] - pre[pa[j][k]];
merge(st, vt[i], vt[j]);
prt.clear(), stt.clear();
for(int x : st) vis[x] = true;
for(int i = 1, sz = st.size(); i < sz; i++) {
int pa = lca(st[i - 1], st[i]);
if(!vis[pa]) vis[pa] = true, prt.push_back(pa);
}
merge(stt, prt, st);
for(int i = 1, u, v, sz = stt.size(); i < sz; i++) {
v = stt[i], u = lca(stt[i - 1], stt[i]);
ans[id] -= (pre[v] - pre[u]);
}
mem[{i, j}] = ans[id];
for(int x : stt) vis[x] = false;
}
}
}
for(int i = 1; i <= q; i++, el) cout << ans[i];
}

浙公网安备 33010602011771号