![BZOJ 1925[Sdoi2010]地精部落 题解 BZOJ 1925[Sdoi2010]地精部落 题解](https://image.shishitao.com:8440/aHR0cHM6Ly9ia3FzaW1nLmlrYWZhbi5jb20vdXBsb2FkL2NoYXRncHQtcy5wbmc%2FIQ%3D%3D.png?!?w=700&webp=1)
题目大意:
1~n的排列中,要任意一个数要么比它左右的数都大或小,求所有的方案数。
思路:
主要思路:离散。
三个引理:
①在n->n-1的转化过程中,我们删除了一个点后,我们可以将n-1个点视为仍是1~n-1的排列。
②在若排列Pn为一个合法抖动子序列,则交换i∈[1,n)与i+1,必能得到另一个抖动子序列。
③抖动序列的对称性,若存在第一段上升的长度为n的抖动子序列,则以n+1-x代x必能得到一个第一段下降的长度为n的抖动子序列。
fi,j表示有个大小为i的数集{1...i},然后开头可以是1到j中的任何一个,但是需要是山峰。然后f表示方案数。
然后转移方程fi,j−1部分就是开头为1~j−1先算出来了,方案数加进来。
f{i-1,i-j}则是开头选了j,剩下i−1个数,离散化一下是一个大小为i−1的数集,然后开头必须选1~j−1来保证下降。但是f表示上升,所以取反,然后应该等于fi−1,i−j
另外注意,这题卡内存,64MB,需要滚动数组。
参考:http://blog.csdn.net/ta201314/article/details/41380891
http://blog.csdn.net/vmurder/article/details/44604275
代码:
#include<cstdio>
#include<iostream>
using namespace std;
int n,i,j,k,mo,dp[][]; int main()
{
scanf("%d%d",&n,&mo);
for (dp[][]=,i=;i<=n;i++)
for (j=;j<=i;j++)
dp[k=i&][j]=(dp[!k][i-j]+dp[k][j-])%mo;
printf("%d\n",(dp[k][n]<<)%mo);
return ;
}