描述
蚯蚓幼儿园有 n 只蚯蚓。幼儿园园长神刀手为了管理方便,时常让这些蚯蚓们列队表演。
所有蚯蚓用从 1 到 n 的连续正整数编号。每只蚯蚓的长度可以用一个正整数表示,根据入园要求,所有蚯蚓的长度都不超过 6 。神刀手希望这些蚯蚓排成若干个队伍,初始时,每只蚯蚓各自排成一个仅有一只蚯蚓的队伍,该蚯蚓既在队首,也在队尾。
神刀手将会依次进行 m 次操作,每个操作都是以下三种操作中的一种:
-
给出 i 和 j ,令 i 号蚯蚓与 j 号蚯蚓所在的两个队伍合并为一个队伍,具体来说,令 j 号蚯蚓紧挨在 i 号蚯蚓之后,其余蚯蚓保持队伍的前后关系不变。
-
给出 i ,令 i 号蚯蚓与紧挨其后的一只蚯蚓分离为两个队伍,具体来说,在分离之后, i 号蚯蚓在其中一个队伍的队尾,原本紧挨其后的那一只蚯蚓在另一个队伍的队首,其余蚯蚓保持队伍的前后关系不变。
-
给出一个正整数 k 和一个长度至少为 k 的数字串 s ,对于 s 的每个长度为 k 的连续子串 t (这样的子串共有 ∣s∣−k+1 个,其中 ∣s∣ 为 s 的长度),定义函数 f(t),询问所有这些 f(t) 的乘积对 998244353 取模后的结果。其中 f(t) 的定义如下:
对于当前的蚯蚓队伍,定义某个蚯蚓的向后 k 数字串为:从该蚯蚓出发,沿队伍的向后方向,寻找最近的 k 只蚯蚓(包括其自身),将这些蚯蚓的长度视作字符连接而成的数字串;如果这样找到的蚯蚓不足 k 只,则其没有向后k数字串。例如蚯蚓的队伍为 10 号蚯蚓在队首,其后是 22 号蚯蚓,其后是 3 号蚯蚓(为队尾),这些蚯蚓的长度分别为 4 、 5 、 6 ,则 10 号蚯蚓的向后 3 数字串为 456
, 22 号蚯蚓没有向后 3 数字串,但其向后 2 数字串为 56
,其向后 1 数字串为 5
。
而 f(t) 表示所有蚯蚓中,向后 k 数字串恰好为 t 的蚯蚓只数。
输入描述
输入文件的第一行有两个正整数 n,m ,分别表示蚯蚓的只数与操作次数。
第二行包含 n 个不超过 6 的正整数,依次表示编号为 1,2,…,n 的蚯蚓的长度。
接下来 m 行,每行表示一个操作。每个操作的格式可以为:
-
1
i j(1≤i,j≤n)表示:令 i 号与 j 号蚯蚓所在的两个队伍合并为一个队伍,新队伍中, j 号蚯蚓紧挨在 i 号蚯蚓之后。保证在此操作之前, i 号蚯蚓在某个队伍的队尾,j 号蚯蚓在某个队伍的队首,且两只蚯蚓不在同一个队伍中。 -
2
i(1≤i≤n)表示:令 i 号蚯蚓与紧挨其后一个蚯蚓分离为两个队伍。保证在此操作之前, i 号蚯蚓不是某个队伍的队尾。 -
3
s k(k为正整数,s为一个长度至少为k的数字串)表示:询问 s 的每个长度为 k 的子串 t 的 f(t) 的乘积,对 998244353 取模的结果。 f(t) 的定义见题目描述。
同一行输入的相邻两个元素之间,用恰好一个空格隔开。
输入文件可能较大,请不要使用过于缓慢的读入方式。
输出描述
依次对于每个形如 3 s k
的操作,输出一行,仅包含一个整数,表示询问的结果。
样例输入
5 9 3 1 3 5 3 3 333135 2 3 333135 1 1 1 3 1 2 5 1 3 2 1 5 4 3 333135 2 3 333135 1 3 333135 3
样例输出
0 81 1 81 0
数据范围与提示
保证 n≤2×105,m≤5×105,k≤50 。
设 ∑∣s∣ 为某个输入文件中所有询问的 s 的长度总和,则 ∑∣s∣≤107 。
设 c 为某个输入文件中形如 2 i
的操作的次数,则 c≤103 。
每个测试点的详细信息见下表:
测试点编号 | n | m | k | ∑∥s∥ | c | 全为 1 |
---|---|---|---|---|---|---|
1 | =1 | ≤103 | =1 | ≤103 | =0 | No |
2 | ≤20 | ≤40 | ≤10 | ≤103 | =0 | No |
3 | ≤150 | ≤2,000 | ≤50 | ≤103 | ≤103 | No |
4 | ≤500 | ≤600 | ≤50 | ≤103 | =0 | No |
5 | ≤103 | ≤2,000 | ≤50 | ≤103 | ≤103 | No |
6 | ≤5×104 | ≤6×104 | ≤5 | ≤5×104 | ≤103 | No |
7 | ≤5×104 | ≤6×104 | ≤50 | ≤5×104 | =0 | Yes |
8 | ≤5×104 | ≤6×104 | ≤50 | ≤5×104 | =0 | No |
9 | ≤5×104 | ≤6×104 | ≤50 | ≤5×104 | ≤103 | No |
10 | ≤5×104 | ≤8×104 | ≤50 | ≤2.5×106 | =0 | No |
11 | ≤5×104 | ≤8×104 | ≤50 | ≤2.5×106 | ≤103 | No |
12 | ≤105 | ≤1.1×105 | ≤6 | ≤105 | ≤103 | No |
13 | ≤105 | ≤1.1×105 | ≤50 | ≤105 | =0 | Yes |
14 | ≤105 | ≤1.1×105 | ≤50 | ≤105 | =0 | No |
15 | ≤105 | ≤1.1×105 | ≤50 | ≤105 | ≤103 | No |
16 | ≤105 | ≤1.5×105 | ≤50 | ≤5×106 | =0 | No |
17 | ≤105 | ≤1.5×105 | ≤50 | ≤5×106 | ≤103 | No |
18 | ≤2×105 | ≤5×105 | =1 | ≤107 | =0 | No |
19 | ≤2×105 | ≤5×105 | =1 | ≤107 | ≤103 | No |
20 | ≤2×105 | ≤2.5×105 | ≤7 | ≤2×105 | ≤103 | No |
21 | ≤2×105 | ≤2.5×105 | ≤50 | ≤2×105 | =0 | Yes |
22 | ≤2×105 | ≤2.5×105 | ≤50 | ≤2×105 | =0 | No |
23 | ≤2×105 | ≤2.5×105 | ≤50 | ≤2×105 | ≤103 | No |
24 | ≤2×105 | ≤3×105 | ≤50 | ≤107 | =0 | No |
25 | ≤2×105 | ≤3×105 | ≤50 | ≤107 | ≤103 | No |
如果一个测试点的“全为1
”的一列为“Yes”,表示该测试点的所有蚯蚓的长度均为 1,并且所有询问串 s 的每一位也均为1
。
代码实现:
#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 ;
}