BZOJ1925: [Sdoi2010]地精部落

【传送门:BZOJ1925


简要题意:

  给出n个格子,要求每个格子的数均为1到n,且每种数字只出现一次,要求能够使这n个格子能够成为抖动数列的放置方法

  抖动数列就是指数列中的每一个数,要么比相邻的数都小(原题表示为山谷),要么比相邻的数都大(原题表示为山峰)


题解:

  毒瘤DP!!!活生生的思维两小时,代码两分钟的毒瘤题!

  一开始想这道题就想到设f[i][j]为处理到第i个格子时,a[i]为j且使得a[i]为山峰的情况数

  结果发现转移不了

  听取了Hanks_o大佬的话,膜了一发题解

  设f[i][j]为处理到第i个格子时,首位的数取值范围为1到j并且使得最后两个数递增的情况数

  g[i][j]为处理到第i个格子时,首位的数取值范围为1到j并且使得最后两个数递减的情况数

  那么对于f[i][j]时的所有情况的数列的每个数k都换成i-j+1的话,就相当于g[i][i-j+1]

  所以f[i][j]=g[i][i-j+1],g[i][j]=f[i][i-j+1]

  ∵$f[i][j]=\sum_{k=1}^{j-1}g[i-1][k]$,$g[i][j]=f[i][i-j+1]$

  ∴$g[i-1][k]=f[i-1][i-k]$

  ∴$f[i][j]=\sum_{k=1}^{j-1}f[i-1][i-k]$

  ∴$f[i][j]=\sum_{k=i-j+1}^{i-1}f[i-1][k]$

  ∴$f[i][j-1]=\sum_{k=i-j+2}^{i-1}f[i-1][k]$

  ∴$f[i][j]=f[i][j-1]+f[i-1][i-j+1]$

  然后就可以转移啦啦啦!!

  要滚动数组不然MLE


参考代码:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
int f[2][5100];
int main()
{
    int n,p;
    scanf("%d%d",&n,&p);
    memset(f,0,sizeof(f));
    int now=0;
    f[0][1]=1;
    for(int i=2;i<=n;i++)
    {
        memset(f[now^1],0,sizeof(f[now^1]));
        for(int j=1;j<=i;j++)
        {
            f[now^1][j]=(f[now^1][j-1]+f[now][i-j+1])%p;
        }
        now^=1;
    }
    int ans=0;
    for(int j=1;j<=n;j++) ans=(ans+f[now][j])%p;
    printf("%d\n",(ans*2)%p);
    return 0;
}

 

posted @ 2018-01-09 13:26  Star_Feel  阅读(123)  评论(0编辑  收藏  举报