题意:有一些蚯蚓,每个蚯蚓都有一个小于等于6的值,有m次操作
操作1:将两队蚯蚓连到一起
操作2:在蚯蚓x的队列中将x及x之前和x之后分为两队
操作3:给一个字符串s和整数k,对于s每个长度为k的字串,看有多少队蚯蚓中有这个子串,将每个子串的值乘起来作为答案
观察数据,有一个条件是k<=50,所以直接采用哈希做,每次合并就暴力枚举新产生的长度<=50的串,最多250个,拆队同理,用哈希表去维护保证正确性,询问时维护长度为k的子串哈希值即可
对于蚯蚓的前后关系,可以用链表维护
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; typedef long long ll; const int mod=998244353; const int P=19260817; const int N=P+10; const int M=2e5+5; const int B=17; int read() { int ret=0,op=1,c=getchar(); while(c>'9' || c<'0') { if(c=='-') op=-1; c=getchar(); } while(c>='0' && c<='9') { ret=ret*10+c-'0'; c=getchar(); } return ret*op; } int ecnt=0,head[N]; int n,m; int nxt[M],lst[M],val[M];//nxt和lst为维护蚯蚓位置的链表 ll powB[100],pow10[100],qwq[100]; char s[10000005]; struct edge { int next,cnt,last; ll w; }e[N]; void add(ll hash,ll q)//哈希表,如果有对于hash这个哈希值有q这个原值则cnt++,否则新开一个点 { //printf("add:%lld:%lld\n",hash,q); for(int i=head[hash];i;i=e[i].next) if(e[i].w==q) { e[i].cnt++; return ; } e[++ecnt].cnt=1; e[ecnt].w=q; e[ecnt].next=head[hash]; head[hash]=ecnt; } void dlt(ll hash,ll q)//与add同理 { for(int i=head[hash];i;i=e[i].next) if(e[i].w==q) { e[i].cnt--; return ; } } int query(ll hash,ll q)//与add同理 { for(int i=head[hash];i;i=e[i].next) if(e[i].w==q) return e[i].cnt; return 0; } int s1[100],s2[100]; void link(int u,int v) { int l1=0,l2=0; ll hsh=0,q=0,haxi=0,Q=0; nxt[u]=v; lst[v]=u; for(int i=u;i&&l1<49;i=lst[i]) s1[++l1]=val[i],hsh=(hsh+val[i]*powB[l1-1])%P,q=q+val[i]*pow10[l1-1];//将u与u之前最长50的串取出来,注意在s1中的串是反着的 for(int i=v;i&&l2<49;i=nxt[i]) s2[++l2]=val[i]; for(int i=l1;i>=1;i--)//s1是反串,最前面的字符在l1的位置,所以倒序枚举 { haxi=0;Q=0; for(int j=1;j<=l2&&i+j<=50;j++) { haxi=(haxi*B+s2[j])%P; Q=Q*10+s2[j]; add((hsh*powB[j]%P+haxi)%P,q*pow10[j]+Q); } hsh=((hsh-powB[i-1]*s1[i])%P+P)%P; q=q-pow10[i-1]*s1[i]; } } void cut(int u,int v)//与link同理 { int l1=0,l2=0; ll hsh=0,q=0,haxi=0,Q=0; nxt[u]=0; lst[v]=0; for(int i=u;i&&l1<49;i=lst[i]) s1[++l1]=val[i],hsh=(hsh+val[i]*powB[l1-1])%P,q=q+val[i]*pow10[l1-1]; for(int i=v;i&&l2<49;i=nxt[i]) s2[++l2]=val[i]; for(int i=l1;i>=1;i--) { haxi=0;Q=0; for(int j=1;j<=l2&&i+j<=50;j++) { haxi=(haxi*B+s2[j])%P; Q=Q*10+s2[j]; dlt((hsh*powB[j]%P+haxi)%P,q*pow10[j]+Q); } hsh=((hsh-powB[i-1]*s1[i])%P+P)%P; q=q-pow10[i-1]*s1[i]; } } int main() { n=read(),m=read(); for(int i=1;i<=n;i++) val[i]=read(),qwq[val[i]]++; powB[0]=pow10[0]=1; for(int i=1;i<=50;i++) powB[i]=powB[i-1]*B%P,pow10[i]=pow10[i-1]*10; int op,x,y; while(m--) { op=read(); if(op==1) { x=read(),y=read(); link(x,y); } else if(op==2) { x=read(); cut(x,nxt[x]); } else { int k; scanf("%s%d",s+1,&k); int len=strlen(s+1); ll hsh=0,q=0,ans=1; if(k==1)//特别的,如果询问长度为1的串,因为之前我也没有加过,所以在读入的时候开一个桶来统计答案 { for(int i=1;i<=len;i++) ans=ans*qwq[s[i]-'0']%mod; printf("%lld\n",ans%mod); continue; } for(int i=1;i<=k;i++) { hsh=(hsh*B+s[i]-'0')%P; q=q*10+s[i]-'0'; } // printf("(%lld)%lld->",hsh,q); ans=query(hsh,q)%mod; for(int i=k+1;i<=len;i++) { hsh=(((hsh-(s[i-k]-'0')*powB[k-1])%P+P)%P*B+s[i]-'0')%P; q=((q-(s[i-k]-'0')*pow10[k-1]))*10+s[i]-'0'; // printf("(%lld)%lld->",hsh,q); ans=ans*query(hsh,q)%mod; } printf("%lld\n",ans); } } return 0; }