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\n", (f[n & 1][n][0] + f[n & 1][n][1]) % P);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值