zoukankan      html  css  js  c++  java
  • [TJOI2018]游园会

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

    1. 长度为(n)且只出现过'N','O','I'三种字符
    2. 和一个长度为(k)的模式串的最长公共子序列长度恰好为(i)
    3. 不含"NOI"这个子串

    题解:DP套DP。
    首先考虑最长公共子序列怎么求。令(dp[i][j])表示当前串的第(i)位,模式串的第(j)位为止最长公共子序列长度为多少,转移方程显然,(dp[i][j])可以滚动数组优化。
    (f[u][i][j])表示已经从(1)(n)计算到第(u)个字符,当前(dp[u])的状态(i),”NOI”中以完成前(j)位的方案数,(u)可以滚动数组优化空间,关于(j)的转移方程显然。对于(i)的转移,只要进行最长公共子序列的动态规划转移即可。
    考虑优化(i)的空间。对于(dp[i][j])(dp[i][j-1]),前者至多比后者多(1),所以可以差分(dp[u]),然后二进制压缩空间。
    (6000 ms) 能过。

    代码:

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    using namespace std;
    typedef long long ll;
    const ll mod = 1e9 + 7;
    int n, k, sz[32768], a[40], b[40];
    char T[40];
    int Now = 0;
    ll ans[40], f[2][32768][3];
    
    inline int Max(int x, int y) { return x > y? x : y; }
    void init() {
    	scanf("%d%d%s", &n, &k, T + 1);
    	for(int i = 1; i < (1 << k); i++)
    		sz[i] = sz[i / 2] + (i & 1);
    	f[Now][0][0] = 1;
    }
    void del_hsh(int x) {
    	for(int i = 0; i < k; i++) a[i + 1] = (x >> i) & 1;
    	for(int i = 1; i <= k; i++)	a[i] += a[i - 1];
    }
    int get_hsh() {
    	int sum = 0;
    	for(int i = 0; i < k; i++) sum |= (b[i + 1] - b[i]) << i;
    	return sum;
    }
    void dp(int u, int x, int h, char c, ll val) {
    	del_hsh(x);
    	for(int i = 1; i <= k; i++)
    		b[i] = Max(a[i], Max(b[i - 1], a[i - 1] + (c == T[i])));
    	int y = get_hsh(); f[u][y][h] = (f[u][y][h] + val) % mod;
    }
    void solve() {
    	for(int i = 1; i <= n; i++) {
    		int New = Now ^ 1;
    		for(int j = 0; j < (1 << k); j++)
    			for(int h = 0; h < 3; h++) f[New][j][h] = 0;
    		for(int j = 0; j < (1 << k); j++) {
    			if(f[Now][j][0])
    				dp(New, j, 1, 'N', f[Now][j][0]),
    				dp(New, j, 0, 'O', f[Now][j][0]),
    				dp(New, j, 0, 'I', f[Now][j][0]);
    			if(f[Now][j][1])
    				dp(New, j, 1, 'N', f[Now][j][1]),
    				dp(New, j, 2, 'O', f[Now][j][1]),
    				dp(New, j, 0, 'I', f[Now][j][1]);
    			if(f[Now][j][2])
    				dp(New, j, 1, 'N', f[Now][j][2]),
    				dp(New, j, 0, 'O', f[Now][j][2]);
    		}
    		Now = New;
    	}
    }
    void print() {
    	for(int i = 0; i < (1 << k); i++)
    		for(int j = 0; j < 3; j++)
    			ans[sz[i]] = (ans[sz[i]] + f[Now][i][j]) % mod;
    	for(int i = 0; i <= k; i++) printf("%lld
    ", ans[i]);
    }
    int main() {
    	init(),solve(),print();
    	return 0;
    }
    
  • 相关阅读:
    linux_一些shell命令分析记录
    linux shell if
    linux_磁盘挂载
    远程工具记录
    oracle_多字段统计(多count)
    tomcat_日志打印格式问题
    cgo -rpath指定动态库路径
    Ubuntu下两个gcc版本切换
    [转]Go与C语言的互操作
    [转]【流媒體】H264—MP4格式及在MP4文件中提取H264的SPS、PPS及码流
  • 原文地址:https://www.cnblogs.com/daniel14311531/p/10205588.html
Copyright © 2011-2022 走看看