[模板] dp套dp && bzoj5336: [TJOI2018]party

Description

Problem 5336. -- [TJOI2018]party

Solution

神奇的dp套dp...

考虑lcs的转移方程:

\[lcs[i][j]=\begin{cases} lcs[i-1][j-1]+1 & (t[i]==s[j]) \\ \max (lcs[i-1][j],lcs[i][j-1]) \end{cases} \]

我们发现 \(lcs[i][j]-lcs[i][j-1] \le 1\),而且\(\left| S \right| \le 15\)
所以我们可以对lcs[i]差分之后状压到一个数\(a\).

先不考虑连续NOI的限制.
设dp[i][a]表示兑奖串长为i,且lcs[i]=a的方案数.
那么我们可以转移:

\[\begin{cases} dp[0][0]=1 \\ dp[i][a]=\sum\limits_{c=1}^3 dp[i-1][trans[a][c]] \end{cases} \]

其中trans[a][c]表示a状态(lcs[i])加上字符c的状态(lcs[i+1]), 这个可以先解码出原lcs数组的值, 再像普通lcs一样维护, 再编码回去.

另外就是细节:

  • 串中没有连续的NOI: dp数组加一维即可
  • 卡空间, 滚动数组

详见代码.

Code

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<set>
#include<map>
using namespace std;
#define rep(i,l,r) for(register int i=(l);i<=(r);++i)
#define repdo(i,l,r) for(register int i=(l);i>=(r);--i)
#define il inline
typedef double db;
typedef long long ll;

//---------------------------------------
const int nsz=1050,ksz=20,k2sz=4e4,nmod=1e9+7;
int n,k,bnd,line[ksz],ans[ksz];
char s[ksz];

int ne[4]{0,1,2,3};
int dp[2][k2sz][3],cur=1;//0:x 1:1 2:12
int trans[k2sz][4],cnt1[k2sz];

int val[2][ksz];
int sol1(int st,int c){//res=st+c
	rep(i,1,k)val[0][i]=val[0][i-1]+((st>>(i-1))&1);
	rep(i,1,k){
		if(c==line[i])val[1][i]=val[0][i-1]+1;
		else val[1][i]=max(val[0][i],val[1][i-1]);
	}
	int res=0;
	rep(i,1,k)res|=((val[1][i]-val[1][i-1])<<(i-1));
	return res;
}
void add(int &a,int b){a=(a+b)%nmod;}
void sol(){
	//init
	bnd=(1<<k)-1;
	rep(i,0,bnd){
		if(i)cnt1[i]=cnt1[i&(i-1)]+1;
		rep(j,1,3)trans[i][j]=sol1(i,j);
	}
	dp[cur][0][0]=1;
	rep(i,1,n){
		cur^=1;
		memset(dp[cur],0,sizeof(dp[cur]));
		rep(j,0,bnd){
			rep(a,0,2){//dp[cur^1][j][a]
				if(a!=2)add(dp[cur][trans[j][ne[a+1]]][a+1],dp[cur^1][j][a]);
				if(a!=0)add(dp[cur][trans[j][1]][1],dp[cur^1][j][a]);
				rep(c,2,3){
					if(c==ne[a+1])continue;
					add(dp[cur][trans[j][c]][0],dp[cur^1][j][a]);
				}
			}
		}
	}
	rep(i,0,bnd){
		rep(j,0,2){
			add(ans[cnt1[i]],dp[cur][i][j]);
		}
	}
}

int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n>>k>>(s+1);
	rep(i,1,k)line[i]=(s[i]=='N'?1:s[i]=='O'?2:3);
	sol();
	rep(i,0,k)cout<<ans[i]<<'\n';
	return 0;
}

posted @ 2019-01-19 10:31  Ubospica  阅读(268)  评论(0编辑  收藏  举报