Bzoj 1925 [Sdoi2010]地精部落 题解
1925: [Sdoi2010]地精部落
Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 1519 Solved: 952
[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
Source
最近做题都是那种想到了秒A,想不到刚一天也打不出来的题啊……
这道题当时想的是两个点两个点的转移,因为如果不去考虑山脉两端的话把它们放在任意一个山峰两侧都是合法的。然后就不知道怎么搞了。
事实证明我还是太菜了……
我们设f[i][j]为我们选i个数(大小无限制),其中第j小的数放在开头且他为山谷的方案数,那么它可以从g[i-1][k](j<=k<=i-1),g数组表示i个数中第k小的数在开头且他为山峰的方案数。这们转移的实际意义就是把j放在那个山峰后面。为什么这样合法呢?因为如果一个数是i个数里第j小的,那么他一定是不比i-1个数里第j个数大的。
但是由于这是一个抖动序列,我们让每个位置的高度变为i-x+1,那么我们就可以由f[i][j]的到g[i][i-j+1],将两个式子合并一下就得到了下面只和f有关的转移方程
f[i][j]=∑f[i-1][1~i-j]
所以我们就可以在n^2的时间复杂度内求出所有的f,再把f加和乘以2就是答案。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<algorithm> 6 #include<cmath> 7 #include<string> 8 #include<queue> 9 #define N 5000 10 using namespace std; 11 long long f[2][N]; 12 int n,p; 13 int main(){ 14 scanf("%d%d",&n,&p); 15 int now=1,la=0; 16 f[now][1]=1; 17 for(int i=2;i<=n;i++) 18 { 19 swap(now,la); 20 long long sum=0; 21 memset(f[now],0,sizeof(f[now])); 22 for(int j=i-1;j>0;j--) 23 { 24 sum+=f[la][i-j]; 25 sum%=p; 26 f[now][j]=sum; 27 } 28 } 29 long long ans=0; 30 for(int i=1;i<n;i++) 31 { 32 ans+=f[now][i]; 33 ans%=p; 34 } 35 ans*=2; 36 ans%=p; 37 printf("%lld\n",ans); 38 return 0; 39 }