bzoj1925 [Sdoi2010] 地精部落

题目描述

传说很久以前,大地上居住着一种神秘的生物:地精。

地精喜欢住在连绵不绝的山脉中。具体地说,一座长度为N的山脉H可分为从左到右的N段,每段有一个独一无二的高度Hi,其中Hi是1到N之间的正整数。

如果一段山脉比所有与它相邻的山脉都高,则这段山脉是一个山峰。位于边缘的山脉只有一段相邻的山脉,其他都有两段(即左边和右边)。

类似地,如果一段山脉比所有它相邻的山脉都低,则这段山脉是一个山谷。

地精们有一个共同的爱好——饮酒,酒馆可以设立在山谷之中。地精的酒馆不论白天黑夜总是人声鼎沸,地精美酒的香味可以飘到方圆数里的地方。

地精还是一种非常警觉的生物,他们在每座山峰上都可以设立瞭望台,并轮流担当瞭望工作,以确保在第一时间得知外敌的入侵。

地精们希望这N段山脉每段都可以修建瞭望台或酒馆的其中之一,只有满足这个条件的整座山脉才可能有地精居住。

现在你希望知道,长度为N的可能有地精居住的山脉有多少种。两座山脉A和B不同当且仅当存在一个i,使得Ai≠Bi。由于这个数目可能很大,你只对它除以P的余数感兴趣。

输入输出格式

输入格式:

 

输入文件goblin.in仅含一行,两个正整数N, P。

 

输出格式:

 

输出文件goblin.out仅含一行,一个非负整数,表示你所求的答案对P取余之后的结果。

 

输入输出样例

输入样例#1: 
4 7
输出样例#1: 
3

说明

【数据规模和约定】

对于20%的数据,满足N≤10;

对于40%的数据,满足N≤18;

对于70%的数据,满足N≤550;

对于100%的数据,满足3≤N≤4200,P≤1e9。

 

题解

好题……思维难度省选,代码难度普及-……

大佬真厉害

首先先弄清楚三个性质

$$性质一:在一个波动序列中,如果数字i与i+1不相邻,那么交换i与i+1,得到的仍然是一个波动序列$$

$$证明:若i为山峰,交换后i+1必然也为山峰。同理,若i+1为山谷,交换后i也必为山谷$$

$$若i为山谷,因为i+1与i不相邻,所以i两边的高度都大于i+1,所以交换后i+1小于其两边,仍为山谷。同理,若i+1为山峰,交换后i也为山峰$$

 

$$性质二:把波动序列中的每一个数i换成n+1-i,得到的仍是一个波动序列$$

$$证明:原来是山峰的地方变为山谷,原来是山谷的地方便为山峰,仍是波动序列$$

 

$$性质三:波动序列具有对称性,即将一个波动序列翻转后仍是波动序列$$

$$证明:正确性显然$$

于是就可以考虑如何写状态转移方程了。设$f[i][j]$表示选了$1$到$i$的数,$j$为第一个数,且$j$为山峰

很明显答案为$\sum_{i=1}^n f[n][i]$

如何转移?

先给出方程:$f[i][j]=f[i][j-1]+f[i-1][i-j+1]$

首先,因为由性质一可知,当$j$与$j-1$不相邻时,$j$作为第一个数和$j-1$作为第一个数的方案数相同

又因为$j-1$为山峰,且是第一个数,故$j$与$j-1$必定不相邻

然后,我们来考虑$j$和$j-1$相邻的情况

选了$j$为山峰,则第二个数$j-1$必然为山谷,后面的数为$[1,j-1]$和$[j+1,i]$

问题转变为求$i-1$个数,$j-1$为开头的山谷的方案数

由性质二可得,$j-1$作为山峰和山谷的方案数相同

又因为山峰和山谷是相对位置关系,所以将$[j+1,i]$区间每一个数减一,对方案无影响

于是我们加上$f[i-1][(i-1+1)-(j-1)]$即可

解决了

ps:代码难度和思维难度真心不成正比……

 1 //minamoto
 2 #include<bits/stdc++.h>
 3 #define N 5005
 4 #define inf 0x3f3f3f3f
 5 using namespace std;
 6 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<15,stdin),p1==p2)?EOF:*p1++)
 7 char buf[1<<15],*p1=buf,*p2=buf;
 8 inline int read(){
 9     #define num ch-'0'
10     char ch;bool flag=0;int res;
11     while(!isdigit(ch=getc()))
12     (ch=='-')&&(flag=true);
13     for(res=num;isdigit(ch=getc());res=res*10+num);
14     (flag)&&(res=-res);
15     #undef num
16     return res;
17 }
18 int n,mod,f[2][N];
19 int main(){
20     //freopen("testdata.in","r",stdin);
21     n=read(),mod=read();
22     f[0][2]=1;
23     for(int i=3;i<=n;++i)
24     for(int j=2;j<=i;++j)
25     f[i&1][j]=(f[i&1][j-1]+f[i-1&1][i-j+1])%mod;
26     int ans=0;
27     for(int j=2;j<=n;++j)
28     ans=(ans+f[n&1][j])%mod;
29     printf("%d\n",(ans<<1)%mod);
30     return 0;
31 }

 

posted @ 2018-07-26 16:23  bztMinamoto  阅读(184)  评论(0编辑  收藏  举报
Live2D