我们令 fi f i 表示使得 i i ~合法的期望次数,题目要求的其实就是 min(f1,f2....fn−L+1) m i n ( f 1 , f 2 . . . . f n − L + 1 ) 的期望
我们先考虑怎么求
fi
f
i
,设共有
U
U
种课程,其中有个课程是要上的,相当于
U
U
个白球,其中有个球有标记,每次我们随机取出一个球将他染黑,问将这
L
L
个标记球都染黑的期望次数
我们设表示我们已经染黑了
i
i
个标记球的期望次数,有
可解得 hL=∑Li=1Ui h L = ∑ i = 1 L U i
然后,最大最小值反演这个东西是可以套在期望上的
直接求这个min不好求,但是求max就相当于我们上面求的这个给球染色的期望次数
令
gS
g
S
表示使集合S里的所有区间都合法,需要上的课程数
ans=∑S∑gSi=1Ui
a
n
s
=
∑
S
∑
i
=
1
g
S
U
i
复杂度
O(2n−Ln)
O
(
2
n
−
L
n
)
,当n-L较大的时候会GG
注意到当 L L 比较小的时候,我们可以不枚举S,直接对整个序列dp,表示dp到第i个位置,当前确定要上的课程有m个,前 L L 个区间是否被选的状态为mask,方案数
复杂度,和前面那个算法结合起来就可以AC了
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define lowbit(x) x&(-x)
using namespace std;
const int maxn = 35;
const int maxm = 35;
const int mask = 1<<10;
const int mod = 998244353;
inline void add(int &a,const int &b){a+=b;if(a>=mod)a-=mod;if(a<0)a+=mod;}
int pw(int x,int k)
{
int re=1;
for(;k;k>>=1,x=(ll)x*x%mod) if(k&1)
re=(ll)re*x%mod;
return re;
}
int inv(int x){ return pw(x,mod-2); }
int n,m,num,u;
int s[maxn*maxn];
int v[maxn][30];
int M[maxm],mh[maxn];
bool Check()
{
int ok=0;
for(int i=1;i<=u;i++)
{
int nk=1;
for(int j=0;j<m;j++) if(!v[i+j][M[j]]) { nk=0;break; }
mh[i]=nk;
ok|=nk;
}
return ok;
}
int ans;
int use[maxn][30];
void dfs(int nowi,int k,int sig)
{
if(nowi>u) { sig==1?add(ans,s[k]):add(ans,-s[k]); return; }
dfs(nowi+1,k,sig);
if(!mh[nowi]) return;
for(int j=0;j<m;j++)
{
if(!use[nowi+j][M[j]]) k++;
use[nowi+j][M[j]]++;
}
dfs(nowi+1,k,-sig);
for(int j=0;j<m;j++) use[nowi+j][M[j]]--;
}
int f[2][mask][maxn*maxn];
void dp()
{
int al=1<<m-1;
memset(f,0,sizeof f);
int now=0; f[now][0][0]=-1;
for(int i=1;i<=n;i++)
{
now=!now;
for(int j=0;j<al;j++)
{
int tc=0,tn=0;
for(int k=0;k<m-1;k++) if(j>>k&1)
if(i-m+k+1>0)
{
int cc=M[m-k-1];
if(!(tc>>cc&1)) tc|=1<<cc,tn++;
}
for(int k=0;k<=num;k++) if(f[!now][j][k])
{
int &temp=f[!now][j][k];
add(f[now][j>>1][k+tn],temp);
if(i<=u&&mh[i]) add(f[now][j>>1|(m-2>=0?(1<<m-2):0)][k+tn+!(tc>>M[0]&1)],-temp);
temp=0;
}
}
}
for(int j=0;j<al;j++) for(int k=0;k<=num;k++)
add(ans,(ll)f[now][j][k]*s[k]%mod);
}
char str[maxn];
int main()
{
//freopen("tmp.in","r",stdin);
//freopen("tmp.out","w",stdout);
int tcase; scanf("%d",&tcase);
while(tcase--)
{
memset(v,0,sizeof v); num=0;
scanf("%d%d",&n,&m); u=n-m+1;
for(int i=1;i<=n;i++)
{
scanf("%s",str); int len=strlen(str);
num+=len;
for(int j=0;j<len;j++) v[i][str[j]-'a']=1;
}
scanf("%s",str);
for(int i=0;i<m;i++) M[i]=str[i]-'a';
if(!Check()) { puts("-1"); continue; }
for(int i=1;i<=num;i++) s[i]=(s[i-1]+(ll)num*inv(i)%mod)%mod;
ans=0;
if(m<=10) dp();
else dfs(1,0,-1);
printf("%d\n",ans);
}
return 0;
}