BZOJ1925 [SDOI2010]地精部落
题目部分
题目描述
传说很久以前,大地上居住着一种神秘的生物:地精。
地精喜欢住在连绵不绝的山脉中。具体地说,一座长度为N的山脉H可分为从左到右的N段,每段有一个独一无二的高度\(H\_i\),其中\(H\_i\)是\(1\)到\(N\)之间的正整数。
如果一段山脉比所有与它相邻的山脉都高,则这段山脉是一个山峰。位于边缘的山脉只有一段相邻的山脉,其他都有两段(即左边和右边)。
类似地,如果一段山脉比所有它相邻的山脉都低,则这段山脉是一个山谷。
地精们有一个共同的爱好——饮酒,酒馆可以设立在山谷之中。地精的酒馆不论白天黑夜总是人声鼎沸,地精美酒的香味可以飘到方圆数里的地方。
地精还是一种非常警觉的生物,他们在每座山峰上都可以设立瞭望台,并轮流担当瞭望工作,以确保在第一时间得知外敌的入侵。
地精们希望这N段山脉每段都可以修建瞭望台或酒馆的其中之一,只有满足这个条件的整座山脉才可能有地精居住。
现在你希望知道,长度为N的可能有地精居住的山脉有多少种。两座山脉A和B不同当且仅当存在一个i,使得Ai≠Bi。由于这个数目可能很大,你只对它除以P的余数感兴趣。
输入格式
输入文仅含一行,两个正整数\(N, P\)。
输出格式
输出文件仅含一行,一个非负整数,表示你所求的答案对P取余之后的结果。
输入输出样例
输入 #1
4 7
输出 #1
3
数据范围
对于\(20%\)的数据,满足\(N \leq 10\);
对于\(40%\)的数据,满足$ N \leq 18 $;
对于\(70%\)的数据,满足$ N \leq 550 $;
对于\(100%\)的数据,满足$ 3 \leq N \leq 4200,P \leq 1e9 $。
解题报告
题意理解
这道题目,让我们求出 波动序列 的总个数.
波动序列,感性理解即可,或者就是说,高度 上下起伏 的序列.
算法解析
这道题目,思维难度极大,但是代码难度极低,可以说是专门考思维的题目了.
首先,我们得在题目中,找到合适的性质,来求解本题.
第一个性质.
假如说,高度为\(j\),和高度为\(j-1\)的山, 不相邻 的话,那么交换这两个山,依旧是合法的波动序列.
粗略证明.
我们知道,假如说\(j\),所在的是山峰.
那么我们得出,身旁两个山, 最高 也是 \(j-2\) \(j-3\).
- \(j-1\)和它不相邻
- 山的高度是独一无二的.
因此我们把这个\(j-1\)移动过来,一定也可以构成山峰.
假如说\(j-1\),所在的是山谷.
那么我们得出,对于\(j-1\)而言,两旁的山, 最矮 也是\(j+1\),\(j+2\)
- \(j\)和它不相邻
- 山的高度是独一无二的
因此山谷还是山谷
那么假如说\(j-1\)所在的是山峰呢?
那么\(j-1\)而言,两旁的山,最高也就是 \(j-2\),\(j-3\).
- \(j\)和它不相邻
- 山的高度是独一无二的.
所以我们发现,这个山峰移动过来,同样还是山峰.
证明如上所示,对于\(j\)是山谷,同理可得,我们就不写了.
第二个性质
假如说现在的山们,高度分布为\([1,x]\),那么我们将所有的山,高度统统增加\(1\)
最后我们发现,波动序列不变.
证明:
因为高度关系没有改变,所以波动序列依旧波动.
只不过所有的山都长高了.
第三个性质
假如说\([1,x]\)是一组波动序列,那么我们可以通过映射.
一个\([1,x]\)的波动序列可以映射到一个\([y−x+1,y]\)的波动序列.
你可以认为是所有高度相加\(y-x\).
那么就是第二个性质的一个拓展了.
其实也就是所谓的,波动序列具有对称性质.
比如说:
\(1 3 2 5 4\) 这是一个波动序列
那么
\(4 5 2 3 1\) 同样也是一个波动序列.
方程推导
我们现在把所有的性质,都找出来了,那么接下来就是最难的推导过程了.
我们不妨,设置一下状态表示.
\(f[i][j]表示[1,i]构成的排列中,第一个山峰,高度为j的波动序列的个数\)
请注意,上面说是第一个山峰,不是第一个山谷,也就是说,这第一座山, 必须强制保证 为山峰.
现在我们思考一下,第二座山该放置情况.
假如说,第二座山不是\(j-1\).
也就是说\(j\),和\(j-1\)不是相邻的.
这说明什么?
我们可以使用第一个性质来转移.
因此我们不妨得出.
接下来,我们着重理解一下,第二三性质总和,得出的第二个推导方程.
我们之前分类讨论,讨论了,第二座山不是\(j-1\)的情况,那么接下来,我们着重分析.
第二座山就是\(j-1\)的情况.
此时,我们知道第二座山,必然是 山谷 了.
但是山谷的方案总数,其实和山峰的方案总数是一样的.
因为,每一个点,不是山谷就是山峰,确定了一个点是山谷,那么附近的点就是山峰.
确定了一座山的所属,那么其他山的所属也就确定了.
因此我们知道,\(\text{山谷的方案数}=\text{山峰的方案数}\)
因为第一座山已经确定了,那么后面的山一定在\([1,j−1] \cup [j+1,i]\)这个范围内。
现在第二座山是\(j-1\)了,只不过是山谷,但是上面,我们证明了是$ \text{山谷总数}= \text{山峰总数} $
不过我们知道,山峰的前面都是山谷,山谷的前面都是山峰.
山谷山峰,交替出现.
所以不妨\([j+1,i] => [j,i-1]\)
相当于从山谷,到了山峰.
而且因为性质二,我们知道,其实方案数量是没有变化的,因此相当于统统都\(-1\)了.
接下来通过,性质二变形得到的性质三,将这个\([j,i-1]\)区间,映射到\([1,n]\)开头的区间
这样离散化映射,就是为了,让我们统计出答案.
简而言之就是,所以山的高度,都减少\(j-1\),那么这样的话,\([j,i-1]=>[1,(i-1)-(j-1)]\)
这就是我们的区间离散化,得到的方案.
因此
记得使用滚动数组,使得内存在合适的范围
还有注意一下,根据波动序列具有对称性,所以最后答案要乘以2
代码解析
#include <bits/stdc++.h>
const int N=4210;
int n,p,f[2][N];
int main()
{
scanf("%d%d",&n,&p);
f[1][1]=1;
for (int i=2; i<=n; ++i)
for (int j=1; j<=i; ++j)
f[i&1][j]=(f[i&1][j-1]+f[(i-1)&1][i-j])%p;
printf("%d\n",f[n&1][n]*2%p);
}