正题
题目链接:https://uoj.ac/problem/748
题目大意
有一个长度为nnn的010101序列,然后ttt次插入一个000和一个111,要求000在111前面,求最终能得到多少种本质不同的串。
1≤n,t≤3001\leq n,t\leq 3001≤n,t≤300
解题思路
我们考虑一个n+2×tn+2\times tn+2×t的010101串是否合法,而且我们最好能搞出一种记录信息最少且唯一的方法。
我们记录一个xxx表示当前匹配到的位置,当我们加入一个000或111时,如果恰好能和下一个匹配,我们就匹配。否则如果是000,我们再记录一个yyy表示目前有多少个未匹配的000。如果是111,如果前面有未匹配的000,我们就用未匹配的000和这个111匹配。
如果没有我们就一直让匹配位置xxx往前走,直到出现一个未匹配的000,我们可以先预处理出一个pip_ipi表示匹配位置iii往前跳到出现第一个未匹配000的位置。
这种匹配方法一定是最优的,因为往前跳一到的位置一定是一个000,而之后我们拿未匹配的000去匹配这个000显然不优秀。
然后我们设fi,j,kf_{i,j,k}fi,j,k表示现在填到第iii个,目前匹配到位置jjj,前面有kkk个未匹配的000时前面填的方案数转移即可。
时间复杂度:O(n3)O(n^3)O(n3)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=305,P=998244353;
int n,t,f[N*3][N][N],pre[N];
char s[N];
void Add(int &x,int y)
{x=(x+y>=P)?(x+y-P):(x+y);}
int main()
{
// printf("%d\n",sizeof(f)>>20);scanf("%d%d",&n,&t);scanf("%s",s+1);pre[0]=-1;for(int i=1;i<=n;i++){int p=0;for(int j=i;j>=1;j--){if(s[j]=='1')p++;else p--;if(p==-1){pre[i]=j-1;break;}}if(p!=-1)pre[i]=-1;}f[0][0][0]=1;for(int i=0;i<n+2*t;i++)for(int j=0;j<=n;j++)for(int k=0;k<=t;k++){if(!f[i][j][k])continue;//zeroif(s[j+1]=='0')Add(f[i+1][j+1][k],f[i][j][k]);else Add(f[i+1][j][k+1],f[i][j][k]);//oneif(s[j+1]=='1')Add(f[i+1][j+1][k],f[i][j][k]);else{if(k)Add(f[i+1][j][k-1],f[i][j][k]);else if(pre[j]!=-1)Add(f[i+1][pre[j]][k],f[i][j][k]);}}printf("%d\n",f[n+2*t][n][0]);return 0;
}