[TJOI2018] 游园会

所谓 dp套dp ,实际上就是在说求解一个 dp 的过程中,我们用另一个 dp 求解出他应该从某个状态转移到另一个状态。

考虑一下这道题,设 \(dp_{i,j}\) 表示兑奖串考虑了前 \(i\) 个,奖章串考虑了前 \(j\) 个,此时的 LCS,则:

\[dp_{i,j}=\max\{dp_{i-1,j},dp_{i,j-1},dp_{i-1,j-1}+[s_i==t_j]\} \]

显然,当 \(i\) 固定的时候,\(dp_{i,j}\) 是单调不降的,且相邻两个数的差 \(\in[0,1]\),考虑直接状压这个东西,其实相当于就是求得当 \(i\) 一定的时候,这个 \(dp\) 数组长什么样。

\(f_{i,S,0/1/2}\) 表示当考虑了兑奖串的前 \(i\) 位的时候,LCS 的 \(dp\) 式子状压下来为 \(S\),最后几位同 NOI 匹配到了第几位,此时的方案数。

\(sta_{S,j}\) 表是当固定了兑奖串那一维后,LCS 的 \(dp\) 式子状压下来为 \(S\),然后在奖章串末尾放入 \(j\) 后的 \(dp\) 式子状压下来的情况,具体求解类似 LCS 的内层 \(dp\)

状态转移:

\[f_{i,sta_{S,x},nxt_{y,x}}\gets f_{i-1,S,y} \]

\(nxt_{y,x}\) 表示NOI 匹配到了前 \(y\) 个后加入 \(x\) 后匹配到的数量,初始状态设 \(f_{0,0,0}=1\)

时间为 \(\mathcal{O}(n2^k+k2^k)\),注意空间。

#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define p_b push_back
#define m_p make_pair
#define pii pair<int,int>
#define fi first
#define se second
#define ls k<<1
#define rs k<<1|1
#define mid ((l+r)>>1)
#define gcd __gcd
#define lowbit(x) (x&(-x))
using namespace std;
int rd(){
    int x=0,f=1; char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())if (ch=='-') f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<1)+(x<<3)+(ch^48);
    return x*f;
}
void write(int x){
    if(x>9) write(x/10);
    putchar('0'+x%10);
}
const int N=1000+5,K=15,INF=0x3f3f3f3f,mod=1e9+7;
void add(int &x,int y){
    x+=y;
    if(x>=mod) x-=mod;
}
int nxt[3][3]={{1,0,0},{1,2,0},{1,0,3}},sta[(1<<K)][3],f[2][(1<<K)][3];
int n,m,dp[2][K+3],b[N],ans[K+3];
int main(){
    n=rd(),m=rd();
    for(int i=1;i<=m;i++){
		char ch=getchar();while(ch<'A'||ch>'Z') ch=getchar();
		if(ch=='N') b[i]=0;
		else if(ch=='O') b[i]=1;
		else b[i]=2;
	}
    for(int s=0;s<(1<<m);s++){
        for(int i=0;i<m;i++) dp[0][i+1]=dp[0][i]+((s>>i)&1);
        for(int c=0;c<=2;c++){
            for(int i=1;i<=m;i++){
                dp[1][i]=max(dp[0][i],dp[1][i-1]);
                if(b[i]==c) dp[1][i]=max(dp[1][i],dp[0][i-1]+1);
            }    
            for(int i=0;i<m;i++) if(dp[1][i+1]>dp[1][i])sta[s][c]|=(1<<i);
        }
    }
    f[0][0][0]=1;int now=1;
    for(int i=0;i<n;i++){
        memset(f[now],0,sizeof(f[now]));
        for(int s=0;s<(1<<m);s++){
            for(int y=0;y<=2;y++){
                if(!f[now^1][s][y]) continue;
                for(int x=0;x<=2;x++){
                    if(nxt[y][x]==3)continue;
                    add(f[now][sta[s][x]][nxt[y][x]],f[now^1][s][y]);
                }
            }
        }
        now^=1;
    }now^=1;
    for(int s=0;s<(1<<m);s++)for(int x=0;x<=2;x++) add(ans[__builtin_popcount(s)],f[now][s][x]);
    for(int i=0;i<=m;i++)printf("%d\n",ans[i]);
    return 0;
}
posted @ 2024-08-20 09:19  123456xwd  阅读(21)  评论(0)    收藏  举报