BZOJ4943 [NOI2017] 蚯蚓
题目描述
蚯蚓幼儿园有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
的操作,输出一行,仅包含一个整数,表示询问的结果。
题目大意:
一个初始全为单个元素的集合,支持合并,分裂,查询s,k求出s所有长度为k的子串在现有的序列中出现的次数的乘积。
题解:
①用链表记录nxt[],pre[]来模拟分裂,合并;
②考虑到k<=50,意思就是我们只需要维护长度在50以内串,外加查询其出现的次数。而分裂操作c并不多,意味着合并也并不多。暴力维护即可:每次合并分裂时枚举跨过断点前后50位以内的串修改。
③用hash散列表来修改和查询,由于hash的模值比较大,无法开下所有v值,就可以将v类似于存边存进v%sz里,每次修改和查询暴力枚举一遍,复杂度几乎为O(1)
1 #pragma GCC optimize(2) 2 #include<cstdio> 3 #include<iostream> 4 #include<cstring> 5 #define N 200010 6 #define M 11000000 7 #define base 1000000007 8 #define mod 998244353 9 #define ull unsigned long long 10 using namespace std; 11 int n,m,pre[N],nxt[N]; 12 char tmp[M],*P = tmp,*s,a[N]; 13 ull H[M],pw[M]; 14 char gc(){ 15 static char *p1,*p2,s[1000000]; 16 if(p1==p2) p2=(p1=s)+fread(s,1,1000000,stdin); 17 return(p1==p2)?EOF:*p1++; 18 } 19 int rd(){ 20 int x = 0,f = 1; char c = gc(); 21 while(c<'0'||c>'9') {if(c=='-') f = -1; c = gc();} 22 while(c>='0'&&c<='9') {x = x * 10 + c - '0'; c = gc();} 23 return x * f; 24 } 25 int rd(char *c){ 26 char *st = c; 27 do *c = gc(); while(*c<'0'||*c>'9'); 28 do *++c = gc(); while(*c>='0'&&*c<='9'); 29 *c = 0; 30 return c - st; 31 } 32 struct Edge{ull v; int nt,w;}; 33 const int sz = 1e7; 34 struct HASH{ 35 Edge E[sz + 1]; int o,hd[sz + 1]; 36 int& adde(ull v){ 37 E[++o] = (Edge){v,hd[v%sz],0}; hd[v%sz] = o; 38 return E[o].w; 39 } 40 int& operator [] (ull v){ 41 for(int i = hd[v%sz];i;i=E[i].nt) 42 if(E[i].v==v) return E[i].w; 43 return adde(v); 44 } 45 int val(ull v){ 46 for(int i = hd[v%sz];i;i=E[i].nt) 47 if(E[i].v==v) return E[i].w; 48 return 0; 49 } 50 }Hash; 51 void cal(int y,int x){ 52 static char S[N]; int len = 0,fg; 53 int st;for(st = y;pre[st]&&len<=50;st = pre[st],len++); fg = ++len; 54 int ed;for(ed = y;nxt[ed]&&len<=100;ed = nxt[ed],len++); 55 len = 0; //S[++len] = a[st]; 56 for(int i = st;nxt[i];i = nxt[i]) S[++len] = a[i]; 57 S[++len] = a[ed]; 58 for(int i = 1;i <= len;i++) H[i] = H[i - 1]*base + S[i]; 59 for(int i = fg;i <= len;i++) 60 for(int j = max(i - 50,0);j < fg - 1;j++){ 61 Hash[H[i] - H[j] * pw[i - j]]+=x; 62 } 63 } 64 int main() 65 { //freopen("mzoj1112.in","r",stdin); 66 //freopen("mzoj1112.out","w",stdout); 67 n = rd(); m = rd(); 68 for(int i = pw[0] = 1;i < M;i++) pw[i] = pw[i - 1] * base; 69 for(int i = 1;i <= n;i++) Hash[a[i] = rd() + '0']++; 70 for(int i = 1,typ,x,y,len,k;i <= m;i++){ 71 typ = rd(); 72 if(typ==1) { 73 x = rd(); y = rd(); 74 pre[y] = x; nxt[x] = y; 75 cal(y,1); 76 } 77 else if(typ==2){ 78 x = rd(); y = nxt[x]; 79 cal(y,-1); 80 pre[y] = nxt[x] = 0; 81 } 82 else{ 83 s = P; len = rd(s+1); P += len + 2; k = rd(); 84 ull ans = 1; 85 for(int i = 1;i <= len;i++){ 86 H[i] = H[i - 1]*base + s[i]; 87 if(i>=k) { 88 ull temp = H[i] - H[i - k] * pw[k]; 89 ans = ans * Hash.val(temp) % mod; 90 } 91 } 92 printf("%llu\n",ans); 93 } 94 } 95 return 0; 96 }//by tkys_Austin;