梦熊2026省选集训 Day5 T1
题意
\(T\) 组询问 \(n,m,w\) 求有多少个长为 \(n\) 的 \(01\) 串 \(s\) 和长为 \(m\) 的字符串 \(t\) 使得 \(s,t\) 存在最长公共子串 \(w\)。
\(n,m\leq 100,|w|\leq 3,T\leq 10\)
思路
我们必须利用 \(|w|\) 非常小的性质。考虑设计动态规划。我们记录 \(f_{i,j,s_1,s_2}\) 表示 \(i,j\) 位的末尾有什么。但是这样我们发现我们不知道在 \(i\) 或 \(j\) 后有没有出现一个长度为 \(|w|+1\) 的相同子串。那么我们记 \(f_{i,j,s_1,s_2,p_1,p_2}\),其中 \(p_1,p_2\) 表示 \(s\) 和 \(t\) 中出现的所有长度为 \(|w|+1\) 的 \(01\) 串有没有出现过。但是这样我们发现不需要记录两个了,直接记录 \(f_{i,s_1,p_1}\) 表示同理。这样我们需要查询 \(n,m\) 就转化成了查询
\[\sum_{i\land j=0}f_{n,i}f_{m,j}
\]
即可。那么我们发现可以对 \(f_n\) 做一个高维前缀和 \(g\)
\[g_i=\sum_{s\in i}f_s
\]
那么我们只需要查寻 \(f_{m,j}\) 中 \(j\) 的补集在 \(g\) 即可。时间复杂度 \(O((n+T)2^{2^{|w|+1}+|w|+1})\)。
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
using namespace std;
typedef long long ll;
const int MAXN=102;
const int MOD=998244353;
int f[MAXN][8][(1<<16)];
int T,n[MAXN],m[MAXN];
string S[MAXN];
void add(int &x,int y){
x+=y;
if(x>=MOD){
x-=MOD;
}
}
int sa[65540],sb[65540],ans[MAXN];
void solve(int len){
memset(f,0,sizeof f);
f[0][0][0]=1;
rep(i,0,99){
rep(j,0,(1<<len)-1){
rep(k,0,(1<<(1<<(len+1)))-1){
if(i<=len-1){
add(f[i+1][(j<<1|1)][0],f[i][j][k]);
add(f[i+1][j<<1][0],f[i][j][k]);
}else{
add(f[i+1][(j<<1|1)&((1<<len)-1)][k|(1<<(j<<1|1))],f[i][j][k]);
add(f[i+1][(j<<1)&((1<<len)-1)][k|(1<<(j<<1))],f[i][j][k]);
}
}
}
}
rep(i,1,T){
if(S[i].size()!=len){
continue;
}
if(n[i]<len){
ans[i]=0;
continue;
}
int num=0;
for(auto v:S[i]){
num=num*2+v-'0';
}
int gv[20];
rep(j,0,(1<<(len+1))-1){
gv[j]=((j>>1)==num||(j&((1<<len)-1))==num);
}
memset(sa,0,sizeof sa);
memset(sb,0,sizeof sb);
rep(j,0,(1<<((1<<(len+1))))-1){
bool fl=false;
rep(k,0,(1<<len+1)-1){
if((j&(1<<k))&&gv[k]){
fl=true;
break;
}
}
if(fl){
rep(k,0,(1<<len)-1){
add(sa[j],f[n[i]][k][j]);
add(sb[j],f[m[i]][k][j]);
}
}else{
add(sa[j],f[n[i]][num][j]);
add(sb[j],f[m[i]][num][j]);
}
}
rep(j,0,(1<<(len+1))-1){
rep(k,0,(1<<(1<<(len+1)))-1){
if((k>>j)&1){
add(sa[k],sa[k^(1<<j)]);
}
}
}
rep(j,0,(1<<(1<<(len+1)))-1){
int lst=((1<<(1<<(len+1)))-1)^j;
add(ans[i],1ll*sa[j]*sb[lst]%MOD);
}
}
}
int main(){
freopen("substring.in","r",stdin);
freopen("substring.out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>T;
rep(i,1,T){
cin>>n[i]>>m[i]>>S[i];
if(n[i]>m[i]){
swap(n[i],m[i]);
}
}
solve(1);
solve(2);
solve(3);
rep(i,1,T){
cout<<ans[i]<<"\n";
}
return 0;
}

浙公网安备 33010602011771号