[TJOI2018]party

题目

传送门 to LOJ

思路

先考虑怎样求最长公共子序列: f ( i , j ) = max ⁡ { f ( i − 1 , j ) ,    f ( i , j − 1 ) ,    f ( i − 1 , j − 1 ) + [ s i = t j ] } f(i,j)=\max\big\lbrace f(i{\rm-1},j),\;f(i,j{\rm-1}),\;f(i{\rm-}1,j{\rm-}1)+[s_i=t_j]\big\rbrace f(i,j)=max{ f(i1,j),f(i,j1),f(i1,j1)+[si=tj]},经典的 d p \tt dp dp 式。

现在考虑将 i i i 不停往前推动。容易发现只需要保存 ∀ j ,    f ( i , j ) \forall j,\;f(i,j) j,f(i,j) 的信息,就可以推出 ∀ j ,    f ( i + 1 , j ) \forall j,\;f(i{\rm+}1,j) j,f(i+1,j) 的信息了。而这一列的值应当如何保存呢?

可以像当初状压 L I S \tt LIS LIS g g g 数组那样,状压差分数组,因为差分值 ∈ [ 0 , 1 ] \in[0,1] [0,1] 。所以我们可以用 2 k 2^k 2k 的状态,找到最长公共子序列。不能出现子串 N O I \rm NOI NOI 就更简单了,再存储一下当前串的结尾已经有了长度为 0 / 1 / 2 0/1/2 0/1/2 N O I \rm NOI NOI 前缀即可。

转移过程中,似乎要多次计算 2 k 2^k 2k 的后继。所以我们可以提前建出 显式状态转移图。当然,从另一个角度来想,我们其实是建立了 D F A \rm DFA DFA,然后在上面做 d p \tt dp dp

时间复杂度 O ( 3 2 n 2 k ) \mathcal O(3^2n2^k) O(32n2k)

代码

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cctype>
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long llong;
inline int readint(){
    
    
	int a = 0, c = getchar(), f = 1;
	for(; !isdigit(c); c=getchar())
		if(c == '-') f = -f;
	for(; isdigit(c); c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}

const int MAXK = 15;
const char table[] = "NOI";
int trans[1<<MAXK][3];
char str[MAXK+5];
void build(int k){
    
    
	static int now[MAXK+1], nxt[MAXK+1];
	for(int S=0; S!=(1<<k); ++S){
    
    
		now[0] = 0; // empty string
		for(int i=0; i!=k; ++i)
			now[i+1] = now[i]+(S>>i&1);
		for(int c=0; c!=3; ++c){
    
    
			int &S_ = trans[S][c] = nxt[0] = 0;
			rep(i,1,k){
    
    
				nxt[i] = max(max(nxt[i-1],now[i]),
					now[i-1]+(str[i]==table[c]));
				S_ ^= (nxt[i]-nxt[i-1])<<i>>1;
			}
		}
	}
}

const int MOD = 1e9+7;
inline void modAddUp(int &x,const int y){
    
    
	if((x += y) >= MOD) x -= MOD;
}
int dp[1<<MAXK][3], nxt[1<<MAXK][3];
int bitcnt[1<<MAXK], ans[MAXK+1];
int main(){
    
    
	int n = readint(), k = readint();
	scanf("%s",str+1); build(k);
	for(dp[0][0]=1; n; --n){
    
    
		for(int S=0; S!=(1<<k); ++S)
			rep(d,0,2) rep(c,0,2){
    
    
				if(d == 2 && c == 2) break; // NOI formed
				int d_ = (c == d) ? (d+1) : (c == 0);
				modAddUp(nxt[trans[S][c]][d_],dp[S][d]);
			}
		memcpy(dp,nxt,(1<<k)*3<<2);
		memset(nxt,0,(1<<k)*3<<2);
	}
	for(int S=0; S!=(1<<k); ++S){
    
    
		bitcnt[S] = bitcnt[S>>1]+(S&1);
		rep(d,0,2) modAddUp(ans[bitcnt[S]],dp[S][d]);
	}
	rep(i,0,k) printf("%d\n",ans[i]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42101694/article/details/122404266