Processing math: 0%

bzoj 1925

一道略有难度的dp

设状态dp[i][j]表示长度为i,开头高度为j且为山峰的方案数

考虑到一个序列是对称的,所以总方案数即为2*\sum_{i=2}^{n}dp[n][i]

这样我们只需考虑转移即可

首先,我们发现,如果两个数ii+1不相邻,那么交换这两个数之后方案数不变

这个...感性理解一下,如果两个数不相邻,若i在原位置是山峰,换成i+1后肯定仍然是山峰,而如果i在原位置是山谷,那么换成i+1后由于i+1并不在i的左右,因此换成i+1后肯定仍为山谷

剩下的同理可证

因此第一个方向就是:假设jj-1不相邻,那么转移就是dp[i][j]=dp[i][j-1](即原来j-1在山峰,然后交换jj-1,方案数不变)

那么如果jj-1相邻呢?

那么需要第二个性质:如果一个序列{a_{i}}是波动序列,那么序列{(n+1)-a_{i}}也是波动序列,且波峰波谷恰好相反

那么如果jj-1相邻,j在首位为波峰,那么j-1必然在第二位为波谷,那么我们只需求出j-1为波谷的方案即可

再根据第二条性质,我们可以将所有以j-1开头为波谷的序列每一位用i-1+1去减,那么就会变成以i-j+1为开头波峰的方案数

因此总转移方程即为dp[i][j]=dp[i][j-1]+dp[i-1][i-j+1]

滚动数组优化一下即可

复制代码
#include <cstdio>
#define ll long long
ll dp[2][4505];
ll n,p;
int main()
{
    scanf("%lld%lld",&n,&p);
    dp[0][2]=1;
    for(int i=3;i<=n;i++)for(int j=2;j<=i;j++)dp[i&1][j]=(dp[i&1][j-1]+dp[(i-1)&1][i-j+1])%p;
    ll ans=0;
    for(int i=2;i<=n;i++)ans=(ans+dp[n&1][i])%p;
    printf("%lld\n",ans*2%p);
    return 0;
}
复制代码

 

posted @   lleozhang  Views(145)  Comments(0Edit  收藏  举报
努力加载评论中...
levels of contents
点击右上角即可分享
微信分享提示