NOI2017 蚯蚓排队(哈希)

「NOI2017」蚯蚓排队

问题描述

蚯蚓幼儿园有$n$只蚯蚓。幼儿园园长神刀手为了管理方便,时常让这些蚯蚓们列队表演。

所有蚯蚓用从 $1$ 到 $n$ 的连续正整数编号。每只蚯蚓的长度可以用一个正整数表示,根据入园要求,所有蚯蚓的长度都不超过 $6$ 。神刀手希望这些蚯蚓排成若干个队伍,初始时,每只蚯蚓各自排成一个仅有一只蚯蚓的队伍,该蚯蚓既在队首,也在队尾。

神刀手将会依次进行 $m$ 次操作,每个操作都是以下三种操作中的一种:

  1. 给出 $i$ 和 $j$ ,令 $i$ 号蚯蚓与 $j$ 号蚯蚓所在的两个队伍合并为一个队伍,具体来说,令 $j$ 号蚯蚓紧挨在 $i$ 号蚯蚓之后,其余蚯蚓保持队伍的前后关系不变。

  2. 给出 $i$ ,令 $i$ 号蚯蚓与紧挨其后的一只蚯蚓分离为两个队伍,具体来说,在分离之后, $i$ 号蚯蚓在其中一个队伍的队尾,原本紧挨其后的那一只蚯蚓在另一个队伍的队首,其余蚯蚓保持队伍的前后关系不变。

  3. 给出一个正整数 $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,\dots,n$ 的蚯蚓的长度。

接下来 $m$ 行,每行表示一个操作。每个操作的格式可以为:

  • 1 $i$ $j$($1 \leq i, j \leq n$)表示:令 $i$ 号与 $j$ 号蚯蚓所在的两个队伍合并为一个队伍,新队伍中, $j$ 号蚯蚓紧挨在 $i$ 号蚯蚓之后。保证在此操作之前, $i$ 号蚯蚓在某个队伍的队尾, $j$ 号蚯蚓在某个队伍的队首,且两只蚯蚓不在同一个队伍中。

  • 2 $i$($1 \leq i \leq 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 \leq 2 \times 10^{5}$,$m \leq 5 \times 10^{5}$,$k \leq 50$ 。

设 $\sum |s|$ 为某个输入文件中所有询问的 $s$ 的长度总和,则 $\sum |s| \leq 10^{7}$ 。

设 $c$ 为某个输入文件中形如2 $i$的操作的次数,则 $c \leq 10^{3}$ 。

每个测试点的详细信息见下表:

测试点编号 $n$ $m$ $k$ $\sum |s|$ $c$ 全为$\texttt{1}$
1 $=1$ $\leq 10^{3}$ $=1$ $\leq 10^{3}$ $=0$ No
2 $\leq 20$ $\leq 40$ $\leq 10$
3 $\leq 150$ $\leq 2,000$ $\leq 50$ $\leq 10^{3}$
4 $\leq 500$ $\leq 600$ $=0$
5 $\leq 10^{3}$ $\leq 2,000$ $\leq 10^{3}$
6 $\leq 5 \times 10^{4}$ $\leq 6 \times 10^{4}$ $\leq 5$ $\leq 5 \times 10^{4}$
7 $\leq 50$ $=0$ Yes
8 No
9 $\leq 10^{3}$
10 $\leq 8 \times 10^{4}$ $\leq 2.5 \times 10^{6}$ $=0$
11 $\leq 10^{3}$
12 $\leq 10^{5}$ $\leq 1.1 \times 10^{5}$ $\leq 6$ $\leq 10^{5}$
13 $\leq 50$ $=0$ Yes
14 No
15 $\leq 10^{3}$
16 $\leq 1.5 \times 10^{5}$ $\leq 5 \times 10^{6}$ $=0$
17 $\leq 10^{3}$
18 $\leq 2 \times 10^{5}$ $\leq 5 \times 10^{5}$ $=1$ $\leq 10^{7}$ $=0$
19 $\leq 10^{3}$
20 $\leq 2.5 \times 10^{5}$ $\leq 7$ $\leq 2 \times 10^{5}$
21 $\leq 50$ $=0$ Yes
22 No
23 $\leq 10^{3}$
24 $\leq 3 \times 10^{5}$ $\leq 10^{7}$ $=0$
25 $\leq 10^{3}$

如果一个测试点的“全为1”的一列为“Yes”,表示该测试点的所有蚯蚓的长度均为1,并且所有询问串$s$的每一位也均为1


注意到这个题$k\leq 50$,那么我们考虑直接暴力维护所有长度在$[1,50]$之间的每种子串的数量,那么考虑用哈希+哈希表来维护每个子串的出现次数。然后修改用链表来实现,每次暴力维护跨过修改位置的子串即可。

这样做的复杂度可以参考知乎lzz的回答


代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#define N 200005
#define M 100000000
#define ull unsigned long long
using namespace std;
char buf[1<<20],*p1,*p2;
#define GC (p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?0:*p1++)
inline void _R(int &x)
{
char t=GC;
while(t<48||t>57)t=GC;
for(x=0;t>47&&t<58;t=GC)x=(x<<3)+(x<<1)+t-48;
}
inline int _S(char *c)
{
char *t=c,ch=GC;
while(ch<48||ch>57)ch=GC;
for(;ch>47&&ch<58;ch=GC)*t++=ch;
*t=0;return t-c;
}
const int Mod=1e7+233;
const int mod=998244353;
const ull pi=1234321237;
int mul(int a,int b){return 1ll*a*b%mod;}
int n,m,A[N],pre[N],nex[N];
ull Pow[N];
char s[N];
struct Hash_table
{
int tot,las[Mod],nex[M],val[M];ull state[M];
int& operator[](ull v)
{
int p=v%Mod;
for(int i=las[p];i;i=nex[i])
if(state[i]==v)return val[i];
state[++tot]=v;
nex[tot]=las[p];
las[p]=tot;
return val[tot];
}
}Q;
int main()
{
int i,j,k,x,y,p,q,ans,l;ull v1,v2;Pow[0]=1;
_R(n);_R(m);
for(i=1;i<=n;i++)_R(A[i]);
for(i=1;i<=50;i++)Pow[i]=Pow[i-1]*pi;
for(i=1;i<=n;i++)Q[A[i]]++;
while(m--)
{
_R(k);
if(k==1)
{
_R(x);_R(y);
pre[y]=x;nex[x]=y;v1=0;
for(i=1,p=x;p&&i<50;i++,p=pre[p])
{
v1*=pi;v1+=A[p];v2=0;
for(j=1,q=y;q&&i+j<=50;j++,q=nex[q])
v2+=Pow[i+j-1]*A[q],Q[v1+v2]++;
}
}
else if(k==2)
{
_R(x);y=nex[x];
pre[y]=nex[x]=0;v1=0;
for(i=1,p=x;p&&i<50;i++,p=pre[p])
{
v1*=pi;v1+=A[p];v2=0;
for(j=1,q=y;q&&i+j<=50;j++,q=nex[q])
v2+=Pow[i+j-1]*A[q],Q[v1+v2]--;
}
}
else
{
l=_S(s+1);_R(x);v1=0;ans=1;
for(i=l;i>l-x+1;i--)v1=v1*pi+s[i]-48;
for(i=l-x+1;i>0;i--)v1=v1*pi+s[i]-48,ans=mul(ans,Q[v1]),v1-=Pow[x-1]*(s[i+x-1]-48);
printf("%d\n",ans);
}
}
}