• SDOI2010 地精部落


    题目链接

    Description

    求长度为 (N) 的排列,满足对于每一个数,要么两边都比他高,要么两边都比他低的的方案数,对 (P) 取模。

    Solution

    AcWing 309. 装饰围栏 的弱化版。

    考虑用 (n - 1) 长度的序列,在右边填上一个数,推导至长度为 (n),相当于长度集合的变化,DP:

    状态设定

    (f_{i, j, p}) 为长度为 (i),最右边的数的排名为 (j) 的方案数,(p = 0 / 1) 表示最右边这个数是山峰还是山谷。

    状态转移

    • 考虑最右边的数是山峰:(f_{i,j,1} gets sum_{k=1}^{j-1} f_{i - 1, k, 0})

    • 考虑最右边的是山谷,同理:(f_{i, j, 0} gets sum_{k=j}^{i - 1} f_{i-1, k, 1})

    转移也可以理解为一个排列的映射:相当于将前 (i - 1) 个序列中数值在 ([j, i - 1]) 的数都 (+1),值域成为 ([j + 1, i]),我们又新填入了一个数 (j),所以是一个 (1) ~ (i) 的排列

    时空复杂度

    时间:注意到这是一个区间和形式,用前缀和优化每次转移 (O(1)),所以总复杂度 (O(N^2))

    空间:这题空间会炸,要开滚动数组。

    #include <iostream>
    #include <cstdio>
    
    using namespace std;
    
    typedef long long LL;
    
    const int N = 4205;
    
    int n, P, f[2][N][2];
    
    int main() {
    	scanf("%d%d", &n, &P);
    	f[1][1][1] = f[1][1][0] = 1;
    	for (int i = 2; i <= n; i++) {
    		for (int j = 1; j <= i; j++) {
    			f[i & 1][j][0] = ((LL)f[(i - 1) & 1][i - 1][1] - f[(i - 1) & 1][j - 1][1] + f[i & 1][j - 1][0] + P) % P;
    			f[i & 1][j][1] = (f[(i - 1) & 1][j - 1][0] + f[i & 1][j - 1][1]) % P;
    		}
    	}
    	printf("%d
    ", (f[n & 1][n][0] + f[n & 1][n][1]) % P);
    	return 0;
    }
    
  • 相关阅读:
    一,安装python
    maven搭建ssm
    web优化
    java代码优化29个点
    供参考的 php 学习路线
    javascript-文档结构遍历
    jquery中的cookie使用
    jQuery中的Ajax
    lambda和抽象类
    上传jar包到nexus私服
  • 原文地址:https://www.cnblogs.com/dmoransky/p/12467931.html
Copyright © 2020-2023  润新知