[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;
}

浙公网安备 33010602011771号