BZOJ 1925: [Sdoi2010]地精部落 dp

来源:互联网 发布:hadoop能用python吗 编辑:程序博客网 时间:2024/05/11 13:31

1925: [Sdoi2010]地精部落

Time Limit: 10 Sec  Memory Limit: 64 MB
Submit: 1384  Solved: 855
[Submit][Status][Discuss]

Description

传说很久以前,大地上居住着一种神秘的生物:地精。 地精喜欢住在连绵不绝的山脉中。具体地说,一座长度为 N 的山脉 H可分 为从左到右的 N 段,每段有一个独一无二的高度 Hi,其中Hi是1到N 之间的正 整数。 如果一段山脉比所有与它相邻的山脉都高,则这段山脉是一个山峰。位于边 缘的山脉只有一段相邻的山脉,其他都有两段(即左边和右边)。 类似地,如果一段山脉比所有它相邻的山脉都低,则这段山脉是一个山谷。 地精们有一个共同的爱好——饮酒,酒馆可以设立在山谷之中。地精的酒馆 不论白天黑夜总是人声鼎沸,地精美酒的香味可以飘到方圆数里的地方。 地精还是一种非常警觉的生物,他们在每座山峰上都可以设立瞭望台,并轮 流担当瞭望工作,以确保在第一时间得知外敌的入侵。 地精们希望这N 段山脉每段都可以修建瞭望台或酒馆的其中之一,只有满足 这个条件的整座山脉才可能有地精居住。 现在你希望知道,长度为N 的可能有地精居住的山脉有多少种。两座山脉A 和B不同当且仅当存在一个 i,使得 Ai≠Bi。由于这个数目可能很大,你只对它 除以P的余数感兴趣。

Input

仅含一行,两个正整数 N, P。

Output

仅含一行,一个非负整数,表示你所求的答案对P取余 之后的结果。

Sample Input

4 7

Sample Output

3

HINT


 
对于 20%的数据,满足 N≤10; 
对于 40%的数据,满足 N≤18; 
对于 70%的数据,满足 N≤550; 
对于 100%的数据,满足 3≤N≤4200,P≤109



不就是膝盖吗,给了 Orz

一个多月前“过”了这道题,还自欺欺人地认为懂了这道题,这直接导致了昨晚多校联测2的T3爆炸,现在想来简直是道水题,不过还是要有“懂得这题怎么做”的前提。。。地精部落这道题可以约化为另一个问题:对于n的排列,告诉你每个数相比于前一个数是大了、小了、还是都可以,求这样的排列的方案数。

先说这一题叭,看过很多其他人的题解,依然是云里雾里,因此我会写的详细一点。我的写法可能与其他人有些不同,但是本质是完全一样的。

首先令f(i, j)表示已经考虑完i的排列了,最后一个数是j,且它为山谷的方案数。这里特别注意!这个i的排列并不一定非要是闭区间[1, i]里的数!这种排列仅仅表示一个大小关系,一种相对的关系,有种离散化的感觉(也可以理解为考虑完i个数了,最后一个数是其中第j小的,且它为山谷的方案数)。这一点非常重要,一定要理解,如不理解可以先往下看,我后面会举个例子。类似地,令g(i, j)表示已经考虑完i的排列了,最后一个数是j,且它为山峰的方案数。那么状态转移方程就是:

    ①

为什么是这样呢?首先f数组的值一定是从g数组转移过来的,因为如果这个是山谷,那么上一个就是山峰。那么为什么等于后面那一串呢?考虑这个例子,7253,这是你填完最后一个数字3后的某个方案。很显然,这种方案应该属于状态f(4, 2),因为已经考虑4个数了,3是其中第2小的。那么f(4, 2)这种状态可以从g(3, 2)与g(3, 3)转移过来,在7253这个例子中,f(4, 2)是从g(3, 2)转移过来的,因为在725中,5是第2小的。那么前i - 1个数中,最小能小到多少呢?(当然是考虑最小的,因为越大,就越可能转移到当前状态,所以最大能大到第i - 1小)答案是能小到j。因为前i个数中第j小的,必然比前i - 1个数中第j小的要小!可以通过刚刚7253这个例子来感受一下,3是前4个数中第2小的,5是前3个数中第2小的。

这个弄懂了之后,我们又可以发现一个很容易发现、非常显然的结论:把一个符合条件的n的排列,对于没一个数i,将其改为n + 1 - i,新的排列依然符合条件,并且原来的山峰变成山谷,山谷变成山峰,因此有:

   ②

联立①②,得

用一个辅助变量s,就可以O(1)转移了,最后ans = (f[n][1] + f[n][2] + ... + f[n][n]) * 2,因为f表示的是最后一个为山谷,根据那个显然的结论,可以得到等量的最后一个为山峰的方案数。在加一个滚动数组压缩空间就可以过了。

转载传送门


#include<cmath>#include<ctime>#include<cstdio>#include<cstring>#include<cstdlib>#include<complex>#include<iostream>#include<algorithm>#include<iomanip>#include<vector>#include<string>#include<bitset>#include<queue>#include<map>#include<set>using namespace std;typedef long long ll;inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch<='9'&&ch>='0'){x=10*x+ch-'0';ch=getchar();}return x*f;}inline void print(ll x){if(x<0)putchar('-'),x=-x;if(x>=10)print(x/10);putchar('0'+x%10);}const int N=4500;int f[N][2];int main(){int n=read(),mod=read();if(n==1){puts("1");return 0;}int now=0;f[1][0]=1;for(int i=2;i<=n;i++){now^=1;for(int j=1;j<=n;j++)if(j<=i)(f[j][now]=f[i-j][now^1]+f[j-1][now])%=mod;else f[j][now]=f[j-1][now];}print((f[n][now]<<1)%mod);puts("");return 0;}/*4 73*/