UNR#6 D1T2 机器人表演 题解

自己切了这题,纪念一下。

Description

有一个长为 \(n\) 的 01 串,你需要计算 \(t\) 次操作后能得到多少不同的 01 串。一次操作的定义为:在串中选两个位置插入一对 01 使得 0 在 1 前,对 998244353 取模。\(n,t \le 300\)。

Solution

考虑给定一个 01 串,怎么判定它合不合法。可以贪心的匹配,如果当前位和 \(S\) 能匹配上则匹配,否则丢到操作里。
但这样显然可能会给后续留下 10 的情况:如令 \(S=0\),则对 010 这个贪心就是错的。
相当于需要从 S 中吐一个 0 出来使之与目前无主的 1 匹配,不妨就让 S 吐出最近的 0。但这个 0 可能后面跟了一些 1,这些 1 显然会失配,也没办法放入 \(t\) 个 01 中,因此我们只能吐出没有和后面 1 匹配的 0,把它之后的 01 对全部加入答案即可。这时假如 S 想要吃进去一部分东西,则吃进去的一定要是括号序列前面加一个 0。而 S 只能在括号序列中吃,所以肯定不行。这个 0 的位置和 01 的数量可以 \(O(n)\) 预处理。
因此可以设计 DP:定义 \(f_{i,j,k}\) 表示填到第 \(i\) 个位置,有 \(j\) 对匹配好的 01 和 \(k\) 个未匹配的 0,则当前匹配到 S 的位置即为 \(i-2j-k\),转移分讨下一位的归属即可。复杂度 \(O((n+t)t^2)\)。

Code

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(int)(b);i++)
using namespace std;
const int N=310,mod=998244353;
void add(int &x,int y){x+=y; if(x>=mod) x-=mod;}
int n,m,f[2][N][N];
int len[N],pos[N];
stack<int> st;
string s;
int main()
{
    ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    cin>>n>>m>>s; s='*'+s;
    rep(i,1,n)
    {
        if(s[i]=='0') st.push(i);
        else if(!st.empty()) st.pop();
        len[i]=st.size();
        if(!st.empty()) pos[i]=st.top();
    }
    f[0][0][0]=1;
    rep(i,0,n+m*2-1)
    {
        int u=i&1,v=u^1;
        rep(j,0,m) rep(k,0,m-j) f[v][j][k]=0;
        rep(j,0,m) rep(k,0,m-j)
        {
            int cur=f[u][j][k];
            if(!cur) continue;
            int p=i-2*j-k;
            if(p==n)
            {
                add(f[v][j][k+1],cur);
                if(k>0) add(f[v][j+1][k-1],cur);
                else if(len[n]&&j+(n-pos[n])/2+1<=m) add(f[v][j+(n-pos[n])/2+1][0],cur);
            }
            else
            {
                add(f[v][j][k],cur);
                if(s[p+1]=='1') add(f[v][j][k+1],cur);
                else if(k>0) add(f[v][j+1][k-1],cur);
                else if(len[p]&&j+(p-pos[p])/2+1<=m) add(f[v][j+(p-pos[p])/2+1][0],cur);
            }
        }
    }
    cout<<f[n&1][m][0]<<"\n";
    return 0;
}
posted @ 2026-08-07 20:16  Antares_qwq  阅读(16)  评论(1)    收藏  举报