题面:luogu3823
题意: n n n只蚯蚓,每只蚯蚓有一个 ≤ 6 \le 6 ≤6的长度,初始时每只蚯蚓一支队伍。给出 m m m个操作。
三种操作:
- 将以 i i i结尾的队伍和以 j j j开头的队伍合并,且 i i i的队伍在前。
- 将 i i i和 i i i后面一只蚯蚓处断开,分为两支队伍。
- 定义以第 x x x只蚯蚓开始的长度为 k k k的数字串为:从 x x x开始的连续 k k k只蚯蚓,它们的长度数字连成的字符串。定义给定一个字符串 s s s和整数 k k k, s s s的每个长度为 k k k的子串 s ′ s' s′,定义 f ( s ′ ) f(s') f(s′)为有多少只蚯蚓开头的长度为 k k k的数字串等于 s ′ s' s′。询问 ∏ s ′ f ( s ′ ) \prod _{s'} f(s') ∏s′f(s′)。
n ≤ 2 × 1 0 5 n \le 2 \times 10^5 n≤2×105, m ≤ 3 × 1 0 5 m \le 3 \times 10^5 m≤3×105, k ≤ 50 k \le 50 k≤50, ∑ ∣ s ∣ ≤ 1 0 7 \sum _{|s|} \le 10^7 ∑∣s∣≤107
题解:首先对于队伍的操作,用双向链表即可维护。
对于数字串,可以用哈希维护。
观察到 k k k的范围很小,意味着每次队伍发生合并或断开时,只会影响到 k k k只蚯蚓的数字串哈希值。
因此每次暴力更新哈希值的复杂度为 O ( k 2 ) O(k^2) O(k2)
建一张哈希表,关键字为哈希值。查询时直接查哈希表即可。
#include <bits/stdc++.h>
using namespace std;
inline int read() {
int x = 0, f = 1; char c; c = getchar() ;
while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar() ;
return x * f ;
}
typedef unsigned long long ull ;
const int maxn = 2e5 + 10, base = 131, mod = 998244353, Hash_mod = 19260817 ;
const int maxs = 1e7 + 10 ;
int a[maxn], nxt[maxn], pre[maxn] ;
ull p[maxn] ;
char str[maxs] ;
struct Hash_table {
int head[Hash_mod], nxt[maxs], len[maxs], val[maxs], tot ;
ull s[maxs] ;
inline void add (ull ss, int l, int v) {
int tmp = ss % Hash_mod ;
for (int i = head[tmp]; i; i = nxt[i])
if (len[i] == l && s[i] == ss) {
val[i] = (val[i] + v) % mod; return ;
}
len[++ tot] = l; val[tot] = v; s[tot] = ss ;
nxt[tot] = head[tmp]; head[tmp] = tot ;
}
inline int query (ull ss, int l) {
int tmp = ss % Hash_mod ;
for (int i = head[tmp]; i; i = nxt[i])
if (len[i] == l && s[i] == ss)
return val[i] ;
return 0 ;
}
}Hash ;
inline void link (int x, int y) {
nxt[x] = y; pre[y] = x ;
ull pres = 0; int tot = 0 ;
for (int i = x, len = 49; i && len; i = pre[i], -- len) {
pres += a[i] * p[tot ++] ;
ull s = pres; int l = tot + 1 ;
for (int j = y; j && l <= 50; j = nxt[j], ++ l) {
s = s * base + a[j]; Hash.add (s, l, 1) ;
}
}
}
inline void cut (int x) {
int y = nxt[x] ;
ull pres = 0; int tot = 0 ;
for (int i = x, len = 49; i && len; i = pre[i], -- len) {
pres += a[i] * p[tot ++] ;
ull s = pres; int l = tot + 1 ;
for (int j = y; j && l <= 50; j = nxt[j], ++ l) {
s = s * base + a[j]; Hash.add (s, l, mod - 1) ;
}
}
nxt[x] = pre[y] = 0 ;
}
inline int query (int k) {
ull s = 0 ;
int len = strlen (str + 1), res = 1 ;
str[0] = '0' ;
for (int i = 1; i < k; i ++) s = s * base + str[i] - '0' ;
for (int i = k; i <= len; i ++) {
s = s * base + str[i] - '0' ;
s -= p[k] * (str[i - k] - '0') ;
res = 1ll * res * Hash.query (s, k) % mod ;
}
return res ;
}
int main() {
int n = read(), m = read() ;
for (int i = 1; i <= n; i ++) {
a[i] = read() ;
Hash.add (a[i], 1, 1) ;
}
p[0] = 1 ;
for (int i = 1; i <= n; i ++) p[i] = p[i - 1] * base ;
while (m --) {
int op = read() ;
if (op == 1) {
int x = read(), y = read() ;
link (x, y) ;
} else if (op == 2) {
int x = read() ;
cut (x) ;
} else {
scanf("%s", str + 1); int k = read() ;
printf("%d\n", query (k)) ;
}
}
return 0 ;
}