首页 > 其他 > 详细

SDOI2010 地精部落

时间:2020-03-12 12:56:48      阅读:40      评论:0      收藏:0      [点我收藏+]

题目链接

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\n", (f[n & 1][n][0] + f[n & 1][n][1]) % P);
    return 0;
}

SDOI2010 地精部落

原文:https://www.cnblogs.com/dmoransky/p/12467931.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!