以后就是 $NOI$ 模拟赛了!!!
T1
一开考就口胡了这道题……
首先遇到这种总字符数特别少的多模板串匹配,肯定要建一个 $AC$ 自动机。
然后想到了维护矩阵,同时记录 转移到的点 和 匹配过的点的集合。
但是这样的复杂度是 $O((50\times m)^3\times log(n))$,会被卡。
所以不能维护 匹配过的点的集合,把它抽到外面去,这样能省很多矩乘复杂度。
不知道你还记不记得这么一道题:GT考试
那一题是不让你匹配出不吉利数字串。
然后回来看看这题,发现两者求的东西其实是相反的。
所以这题可以枚举不出现哪些串,在预处理转移矩阵时,不让根节点能够转移到一个点,使得根节点到这个点的路径组成的串的后缀为一个不能出现的串。
由于转移矩阵经过一次转移,最多多匹配一位,也就是匹配到它的儿子,所以对于一个后缀为一个不能出现的串的串在 $AC$ 自动机上的底部节点(可能不存在,那就不用管),当匹配到这个点的父亲时,判断一下,不让它匹配到这个点就行了。这样就没法转移到这个点及其下面的所有点了。
最后套一个容斥原理就行了:不出现任意一个串的情况数 = 不出现 $1$ 个串的情况数 - 不出现 $2$ 个串的情况数 + 不出现 $3$ 个串的情况数 - 不出现 $4$ 个串的情况数。
答案就是 总情况数 - 不出现任意一个串的情况数。
时间复杂度 $O(2^m\times 50^3\times log(n))$。
另外一定要搞清矩阵的横乘和纵乘是不一样的!!横乘是 $a(i,k)\times b(k,j)$,纵乘是 $b(i,k)\times a(k,j)$!!现场用垃圾纵乘然后写反,成功表演爆 $0$……
1 #include<bits/stdc++.h> 2 #define ll long long 3 #define mod 998244353 4 using namespace std; 5 inline int read(){ 6 int x=0; bool f=1; char c=getchar(); 7 for(;!isdigit(c);c=getchar()) if(c=='-') f=0; 8 for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^'0'); 9 if(f) return x; 10 return 0-x; 11 } 12 int m,mm,n,tag[55]; 13 ll f[55][55],ans; 14 char c[5][55]; 15 16 int que[55],nxt[55]; 17 namespace AC{ 18 int tr[55][26],Tr[55][26],mark[55],cnt; 19 void ins(char ch[55],int id){ 20 int len=strlen(ch),i,u=0,v; 21 for(i=0;i^len;++i){ 22 v=ch[i]^'0'; 23 if(!tr[u][v]) tr[u][v]=Tr[u][v]=++cnt; 24 u=tr[u][v]; 25 } 26 mark[u]|=(1<<id); 27 } 28 void getFail(){ 29 int i,u,hd=1,tl=0; 30 for(int i=0;i^10;++i) if(tr[0][i]) que[++tl]=tr[0][i]; 31 while(hd<=tl){ 32 u=que[hd++], mark[u]|=mark[nxt[u]]; 33 for(i=0;i^10;++i){ 34 if(!tr[u][i]) {tr[u][i]=tr[nxt[u]][i];} 35 else{ 36 que[++tl]=tr[u][i]; 37 nxt[tr[u][i]]=tr[nxt[u]][i]; 38 } 39 } 40 } 41 } 42 }; 43 using namespace AC; 44 void mul(ll a[55][55], ll b[55][55]){ 45 int i,j,k; ll tmp[55][55]; 46 /* cout<<"++"<<endl; 47 for(i=0;i<=cnt;++i){ 48 for(j=0;j<=cnt;++j) 49 printf("%d ",b[i][j]); 50 putchar('\n'); 51 }*/ 52 //cout<<"++"<<endl; 53 for(i=0;i<=cnt;++i) 54 for(j=0;j<=cnt;++j){ 55 tmp[i][j]=0; 56 for(k=0;k<=cnt;++k) 57 (tmp[i][j]+=a[k][j]*b[i][k])%=mod; 58 } 59 for(i=0;i<=cnt;++i) 60 for(j=0;j<=cnt;++j) 61 a[i][j]=tmp[i][j]; 62 } 63 ll a[55][55],b[55][55]; 64 ll qpow(ll x,int y){ 65 ll ret=1; 66 while(y>0){ 67 if(y&1) ret=(ret*x)%mod; 68 x=(x*x)%mod; 69 y>>=1; 70 } 71 return ret; 72 } 73 ll qpow(int x){ 74 int i,j; 75 memset(a,0,sizeof(a)); 76 a[0][0]=1; 77 while(x>0){ 78 if(x&1) mul(a,b); 79 mul(b,b); 80 x>>=1; 81 } 82 ll res=0; 83 for(i=0;i<=cnt;++i) res=(res+a[i][0])%mod; 84 return res; 85 } 86 void dfs(int u,int x,int mx){ 87 if(u){ 88 memset(b,0,sizeof b); 89 int i,j,t; 90 que[1]=0; 91 /* 92 for(int hd=1,tl=1;hd<=tl;++hd){ 93 i=que[hd]; 94 for(j=0;j^m;++j) 95 if(u&(1<<j) && tag[j]==i){ed[u]=1; break;} 96 for(j=0;j^10;++j) 97 if(Tr[i][j]) que[++tl]=Tr[i][j], ed[Tr[i][j]]=ed[i]; 98 }*/ 99 // for(int j=0;j<=cnt;++j)if(mark[j]&u)ed[j]=1; 100 //cout<<"-------"<<endl; 101 for(i=0;i<=cnt;++i){ 102 for(j=0;j^10;++j){ 103 t=tr[i][j]; 104 //printf("back:%d %d %d\n",j,t,i); 105 if(!(mark[t]&u)) b[t][i]=(b[t][i]+1)%mod; 106 } 107 } 108 /*cout<<"-------"<<endl; 109 for(i=0;i<=cnt;++i){ 110 for(j=0;j<=cnt;++j) 111 printf("%d ",b[i][j]); 112 putchar('\n'); 113 }*/ 114 ans+=qpow(n)*x; 115 // printf("Hey buddy:%d %d %lld %lld\n",u,x,haha,ans); 116 ans=(ans%mod+mod)%mod; 117 } 118 if(!(u&1) && (u|1)<mm && 1>mx) dfs(u|1,-x,max(mx,1)); 119 if(!(u&2) && (u|2)<mm && 2>mx) dfs(u|2,-x,max(mx,2)); 120 if(!(u&4) && (u|4)<mm && 4>mx) dfs(u|4,-x,max(mx,4)); 121 if(!(u&8) && (u|8)<mm && 8>mx) dfs(u|8,-x,max(mx,8)); 122 } 123 int main(){ 124 //freopen("password.in","r",stdin); 125 //freopen("password.out","w",stdout); 126 m=read(),n=read(); 127 mm=1<<m; 128 int i,j; 129 for(i=0;i^m;++i){ 130 scanf("%s",c[i]); 131 ins(c[i],i); 132 } 133 getFail(); 134 dfs(0,-1,0); 135 printf("%lld\n",((qpow(10,n)-ans)%mod+mod)%mod); 136 return 0; 137 }