题目大意
有n个人每个人有一个字符集初始为空。
有tot个事件,每个事件形如在第ai个人的字符集里放了bi这个小写字母。
每个时刻等概率发生一个事件。
有一个长度为m的字符串s。某个时刻如果存在编号连续的m个人,按从小到大的顺序发现对于第i个人字符集里都有si,那么则胜利。
问胜利的期望时间,要求判断无解,答案在模意义下进行。
容斥
设
f(i)
表示时刻i还没有胜利的概率。
答案是
∑i>=0f(i)
考虑容斥,枚举哪些位置作为起点在时刻t匹配成功。
f(t)=∑2n−m+1−1i=0g(h(i),t)∗(−1)bitcount(i)
h(i)表示i这个二进制状态内的起点匹配所需要发生的事件总数。
g(i,t)表示长度为t的序列,每个位置是1~tot的数,有i个数一定要出现,概率是多少。
g的计算当然可以容斥
g(i,t)=∑ij=0Cji∗(tot−jtot)t∗(−1)j
发现f的求和最终变成这个地方对
(tot−jtot)t
的求和。
如果是真分数求和当然很好做。
注意j可以为0,那么会出现
1t
求和,怎么办?
只考虑
1t
最终的系数那么是
∑n−m+1i=0Cin−m+1∗(−1)i
容易发现因为
n>=m
这个式子的值是0。
如果没有发现这个,我们还可以换容斥方法。
f(t)=∑2n−m+1−1i=1g(h(i),t)∗(−1)bitcount(i)+1
g(i,t)的意义改为,长度为t的序列,每个位置是1~tot的数,有i个数一定不要全部出现,概率是多少。
此时
g(i,t)=∑ij=1Cji∗(tot−jtot)t∗(−1)j+1
可以发现现在就不带
1t
的项了。
新的式子可以考虑每种情况被计算的次数证明正确性。
回到原来的地方。
然后注意
∑t>=0xt=11−x
,这个可能涉及求逆元,请进行预处理避免复杂度多带一个log。
这个做法的时间复杂度是
O(2n−mtot)
,只有m较大才能用。
对于m较小的情况,不妨设dp[i,s,k]表示确定到第i个位置,(i-m,i)里成为起点的二进制状态为s,带权方案数是多少(因为注意最后有-1的系数)。那么也很好做了,复杂度是
O(2mntot)
,当然因为我好像毛都没预处理所以我的程序是
O(2mnmtot)
。
#include<cstdio>
#include<algorithm>
#include<cstring>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
#define two(i) (1<<(i))
using namespace std;
typedef long long ll;
const int maxn=30+2,mo=998244353;
char s[maxn],h[maxn];
bool dis[maxn][maxn];
int b[maxn][maxn],d[maxn],cnt[maxn*maxn],niy[maxn*maxn];
int c[maxn*maxn][maxn*maxn],f[2][two(15)][maxn*26];
int i,j,k,l,r,t,n,m,now,tot,ni,ans,ca;
bool czy;
int qsm(int x,int y){
if (!y) return 1;
int t=qsm(x,y/2);
t=(ll)t*t%mo;
if (y%2) t=(ll)t*x%mo;
return t;
}
bool pd(){
int i,j;
fo(i,1,n-m+1){
fo(j,1,m+1)
if (j>m||!dis[i+j-1][s[j]-'a']) break;
if (j>m) return 1;
}
return 0;
}
int calc(int x){
if (x==1) return 0;
else return qsm(1-x,mo-2);
}
int g(int cnt){
int i,l,t=0;
fo(i,0,cnt){
l=(ll)c[cnt][i]*/*calc((ll)(tot-i)*ni%mo)%mo*/niy[i]%mo;
if (i%2) (t-=l)%=mo;else (t+=l)%=mo;
}
return t;
}
void dfs(int x,int f,int y){
if (x==n-m+2){
(ans+=f*g(y))%=mo;
return;
}
dfs(x+1,f,y);
int i,t=0;
bool czy=1;
fo(i,x,x+m-1)
if (!dis[i][s[i-x+1]-'a']){
czy=0;
break;
}
else{
b[i][s[i-x+1]-'a']++;
if (b[i][s[i-x+1]-'a']==1) t++;
}
if (czy){
dfs(x+1,-f,y+t);
fo(i,x,x+m-1) b[i][s[i-x+1]-'a']--;
}
}
void brute(){
ans=0;
dfs(1,1,0);
(ans+=mo)%=mo;
printf("%d\n",ans);
}
void solve(){
fo(i,0,1)
fo(j,0,two(m-1)-1)
fo(k,0,tot) f[i][j][k]=0;
f[now=1][0][0]=1;
fo(i,1,n){
fo(j,0,two(m-1)-1)
fo(k,0,tot) f[1-now][j][k]=0;
fo(j,0,two(m-1)-1)
fo(k,0,tot)
if (f[now][j][k]){
fo(t,0,25) d[t]=0;
r=0;
czy=1;
l=j;
fd(t,m,2){
if (l%2==1){
if (!dis[i][s[t]-'a']){
czy=0;
break;
}
d[s[t]-'a']++;
if (d[s[t]-'a']==1) r++;
}
l/=2;
}
if (!czy) continue;
(f[1-now][j/2][k+r]+=f[now][j][k])%=mo;
if (i+m-1<=n&&dis[i][s[1]-'a']){
if (!d[s[1]-'a']) r++;
if (m==1) (f[1-now][0][k+r]-=f[now][j][k])%=mo;
else (f[1-now][j/2+two(m-2)][k+r]-=f[now][j][k])%=mo;
}
}
now=1-now;
}
fo(k,0,tot) cnt[k]=0;
fo(j,0,two(m-1)-1)
fo(k,0,tot) (cnt[k]+=f[now][j][k])%=mo;
ans=0;
fo(k,0,tot) (ans+=(ll)cnt[k]*g(k)%mo)%=mo;
(ans+=mo)%=mo;
printf("%d\n",ans);
}
int main(){
//freopen("ex_B4.in","r",stdin);
scanf("%d",&ca);
while (ca--){
scanf("%d%d",&n,&m);
fo(i,1,n)
fo(j,0,25) dis[i][j]=0;
tot=0;
fo(i,1,n){
scanf("%s",h+1);
t=strlen(h+1);
tot+=t;
fo(j,1,t) dis[i][h[j]-'a']=1;
}
scanf("%s",s+1);
if (!pd()){
printf("-1\n");
continue;
}
ni=qsm(tot,mo-2);
fo(i,1,tot) niy[i]=qsm(1-(ll)(tot-i)*ni%mo,mo-2);
c[0][0]=1;
fo(i,1,tot){
c[i][0]=1;
fo(j,1,i) c[i][j]=(c[i-1][j]+c[i-1][j-1])%mo;
}
if (n-m+1<=16) brute();
else solve();
}
}