zoukankan      html  css  js  c++  java
  • 2883 -- 【TJOI2018】游园会

    Description

    小豆参加了(NOI)的游园会,会场上每完成一个项目就会获得一个奖章,奖章只会是(N,O,I)的字样。在会场。上他收集到了(K)个奖章组成的串。兑奖规则是奖章串和兑奖串的最长公共子序列长度为小豆最后奖励的等级。现在已知兑奖串长度为N,并且在兑奖串上不会出现连续三个奖章为(NOI),即奖章中不会出现子串(NOI)。现在小豆想知道各个奖励等级会对应多少个不同的合法兑奖串。

    Input

    第一行两个数,(N,K)分别代表兑奖串的长度,小豆收集的奖章串的长度。((N≤1000,K≤15))
    第二行一共K个字符,表示小豆得到奖章串。

    Output

    一共(K+1)行,第行表示小豆最后奖励等级为(i−1)的不同的合法兑奖串的个数,可能这个数会很大,结果对(10^9+7)取模。

    Sample Input

    3 2
    NO

    Sample Output

    1
    19
    6

    (DP)维护(DP)。这是个比较套路的题,然后我套路不够深...

    首先我们会想到这是否是个计数类(DP)。然后我们就会想(DP)的状态应该能表示出(LCS)的值。然而我们需要将每个位置的(LCS)值记下来才能得到下一个状态的完整的(DP)值。

    注意到(K)很小,所以我们就记录下在奖章串上每个位置的(DP)值就行了。

    相邻的(DP)值最多差(1),所以我们状压,记录每个位置的(DP)值是否比前一个位置的大就行了。

    类似的题我记得还做过一个要维护最长上升子序列的题,方法类似,就是维护每个元素是否出现在单调栈中。

    #include<bits/stdc++.h>
    #define ll long long
    #define N 1005
    #define M 16
    
    using namespace std;
    inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}
    
    int n,m; 
    const ll mod=1e9+7;
    char s[100];
    int f[1<<M][4],g[1<<M][4];
    int to[4][4];
    char c[]={'N','O','I'};
    int trans[1<<M][4];
    
    void out(int S) {for(int i=0;i<m;i++) cout<<(S>>i&1);}
    void pre() {
    	static int f[M],g[M];
    	for(int S=0;S<(1<<m);S++) {
    		for(int k=0;k<m;k++) f[k+1]=f[k]+(S>>k&1);
    		for(int j=0;j<=2;j++) {
    			for(int k=1;k<=m;k++) {
    				g[k]=max(g[k-1],f[k]);
    				if(c[j]==s[k]) g[k]=max(g[k],f[k-1]+(c[j]==s[k]));
    			}
    			int T=0;
    			for(int k=1;k<=m;k++) if(g[k]>g[k-1]) T|=1<<k-1;
    			trans[S][j]=T;
    		}
    	}
    }
    
    int ans[M];
    int main() {
    	n=Get(),m=Get();
    	scanf("%s",s+1);
    	for(int i=0;i<=2;i++) to[i][0]=1;
    	to[1][1]=2;
    	pre();
    	f[0][0]=1;
    	for(int i=0;i<n;i++) {
    		memset(g,0,sizeof(g));
    		for(int S=0;S<(1<<m);S++) {
    			for(int j=0;j<=2;j++) {
    				for(int now=0;now<=2;now++) {
    					if(now==2&&j==2) continue ;
    					if(!f[S][j]) continue ;
    					(g[trans[S][now]][to[j][now]]+=f[S][j])%=mod;
    				}
    			}
    		}
    		memcpy(f,g,sizeof(f));
    	}
    	for(int S=0;S<(1<<m);S++) {
    		int len=0;
    		for(int j=0;j<m;j++) len+=(S>>j&1);
    		for(int j=0;j<=2;j++) (ans[len]+=f[S][j])%=mod;
    	}
    	for(int i=0;i<=m;i++) cout<<ans[i]<<"
    ";
    	return 0;
    }
    
  • 相关阅读:
    JS 缓冲运动 带运动的留言本 小案例
    ===>===>===>特色思TERSUS元件学习整理
    TERSUS无代码开发(笔记29)-分页(转载)
    TERSUS无代码开发(笔记28)-英文日期改成中文日期设置
    TERSUS无代码开发(笔记27)-按钮加图标
    TERSUS无代码开发(笔记26)-复杂表格中字体通过逻辑设置颜色
    TERSUS无代码开发(笔记25)-弹窗的样式设置
    TERSUS无代码开发(笔记24)-排序显示和高级查询
    TERSUS无代码开发(笔记23)-数据库字段设置
    TERSUS无代码开发(笔记22)-利用CSS制作一个悬浮子菜单(转载)
  • 原文地址:https://www.cnblogs.com/hchhch233/p/10389218.html
Copyright © 2011-2022 走看看