zoukankan      html  css  js  c++  java
  • dp套dp学习笔记(P4590 [TJOI2018]游园会题解)

    dp套dp学习笔记(P4590 [TJOI2018]游园会题解)

    分析

    题目的意思是对于每一个 (i) 求满足如下条件的字符串的数目:

    (1).长度为 (n) 且只出现过'N','O','I'三种字符。

    (2).和一个长度为 (k) 的模式串的最长公共子序列长度恰好为 (i)

    (3).不含"NOI"这个子串。

    发现第一个限制和第三个限制比较好满足,直接 (dp) 的时候记一下当前匹配到了 (NOI) 这个字符串的第几个字符即可。

    对于第二个限制,我们似乎需要知道当前的字符串每一位填了什么数才可以求出它和模式串的最长公共子序列。

    但是仔细想一下又会发现不同的公共子序列只有 (2^k) 种,所以我们只需要状压当前的串已经匹配好的位置的状态。

    转移的时候我们需要对于每一种匹配的状态分别求出它加上三种不同的字符之后达到的新的匹配的状态。

    这个东西可以提前用一次 (dp) 预处理出来。

    转移的时候用我们预处理出来的状态直接转移即可。

    大概可以理解为将内层 (dp) 的结果作为外层 (dp) 的状态进行 (dp)

    代码

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<iostream>
    #define rg register
    inline int read(){
    	rg int x=0,fh=1;
    	rg char ch=getchar();
    	while(ch<'0' || ch>'9'){
    		if(ch=='-') fh=-1;
    		ch=getchar();
    	}
    	while(ch>='0' && ch<='9'){
    		x=(x<<1)+(x<<3)+(ch^48);
    		ch=getchar();
    	}
    	return x*fh;
    }
    const int mod=1e9+7,maxn=4e4+5;
    int nxt[maxn][3],f[2][maxn][3],dp[maxn][2],n,m,a[maxn],mmax,siz[maxn],ans[maxn];
    char s[maxn];
    inline int addmod(rg int now1,rg int now2){
    	return now1+=now2,now1>=mod?now1-mod:now1;
    }
    void pre(){
    	for(rg int i=0;i<=mmax;i++){
    		for(rg int k=1;k<=m;k++) dp[k][0]=dp[k-1][0]+((i>>(k-1))&1);
    		for(rg int j=0;j<=2;j++){
    			for(rg int k=1;k<=m;k++){
    				dp[k][1]=std::max(dp[k-1][1],dp[k][0]);
    				if(a[k]==j) dp[k][1]=std::max(dp[k][1],dp[k-1][0]+1);
    			}
    			rg int zt=0;
    			for(rg int k=1;k<=m;k++) if(dp[k][1]>dp[k-1][1]) zt|=(1<<(k-1));
    			nxt[i][j]=zt;
    		}
    	}
    }
    int main(){
    	n=read(),m=read();
    	scanf("%s",s+1);
    	for(rg int i=1;i<=m;i++){
    		if(s[i]=='O') a[i]=1;
    		else if(s[i]=='I') a[i]=2;
    	}
    	mmax=(1<<m)-1;
    	for(rg int i=1;i<=mmax;i++) siz[i]=siz[i>>1]+(i&1);
    	pre();
    	rg int now=1;
    	f[0][0][0]=1;
    	for(rg int i=1;i<=n;i++){
    		now^=1;
    		for(rg int j=0;j<=mmax;j++){
    			for(rg int k=0;k<=2;k++){
    				f[now^1][j][k]=0;
    			}
    		}
    		for(rg int j=0;j<=mmax;j++){
    			for(rg int k=0;k<=2;k++){
    				if(f[now][j][k]){
    					if(k==0){
    						f[now^1][nxt[j][0]][1]=addmod(f[now^1][nxt[j][0]][1],f[now][j][k]);
    						f[now^1][nxt[j][1]][0]=addmod(f[now^1][nxt[j][1]][0],f[now][j][k]);
    						f[now^1][nxt[j][2]][0]=addmod(f[now^1][nxt[j][2]][0],f[now][j][k]);
    					} else if(k==1){
    						f[now^1][nxt[j][1]][2]=addmod(f[now^1][nxt[j][1]][2],f[now][j][k]);
    						f[now^1][nxt[j][0]][1]=addmod(f[now^1][nxt[j][0]][1],f[now][j][k]);
    						f[now^1][nxt[j][2]][0]=addmod(f[now^1][nxt[j][2]][0],f[now][j][k]);
    					} else {
    						f[now^1][nxt[j][0]][1]=addmod(f[now^1][nxt[j][0]][1],f[now][j][k]);
    						f[now^1][nxt[j][1]][0]=addmod(f[now^1][nxt[j][1]][0],f[now][j][k]);
    					}
    				}
    			}
    		}
    	}
    	now^=1;
    	for(rg int i=0;i<=mmax;i++){
    		ans[siz[i]]=addmod(addmod(ans[siz[i]],f[now][i][0]),addmod(f[now][i][1],f[now][i][2]));
    	}
    	for(rg int i=0;i<=m;i++){
    		printf("%d
    ",ans[i]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    vue慕课网音乐项目手记:9-封装一个公用的scroll组件
    vue慕课网音乐项目手记:50-搜索列表的点击删除、删除全部的交互事件
    vue慕课网音乐项目手记:48-搜索历史数据的处理
    vue慕课网音乐项目手记:6-手写滚动轮播图(中)
    vue慕课网音乐项目手记:5-手写滚动轮播图(上)
    vue慕课网音乐项目手记:30-音乐环形进度条的实现
    vue慕课网音乐项目手记:45-搜索页面跳转歌手页面
    基于Vue2.0的音乐播放器(2)——歌手模块
    linux学习笔记-(1)-安装
    linux学习笔记-前篇
  • 原文地址:https://www.cnblogs.com/liuchanglc/p/14579212.html
Copyright © 2011-2022 走看看