bzoj1925地精部落[SDOI2010](dp)

Posted QuartZ_Z

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj1925地精部落[SDOI2010](dp)相关的知识,希望对你有一定的参考价值。

  题目传送门:1925: [Sdoi2010]地精部落

  这道题,,,首先可以一眼看出他是要我们求由1~n的排列组成,并且抖来抖去的序列的方案数。然后再看一眼数据范围,,,似乎是O(n^2)的dp?然后各种撕烤,,,然而还是不会。。。

  对于这道题,我第一眼的想法是用f[i][j]表示长度为i,最后一个数是j的抖动序列的方案数,然而这是个1~n的排列,似乎没法解决数字重复的问题。。QAQ

  于是看了一波题解,,,原来这个dp是这样子搞的:用f[i][j]表示i个数的排列、第一个数<=j且开头下降的抖动序列的方案数。

  然后dp方程就变成了这样:f[i][j]=f[i][j-1]+f[i-1][i-j]。。。

  当开头的数<j(也就是<=j-1)时,抖动序列的方案数显然=f[i][j-1];

  当开头的数=j 时,我萌尝试把开头的j删掉,然后再把剩下>j的数统统-1,于是我们就会发现,这时的方案和i-1个数的排列、第一个数<=j-1(因为原序列开头是j,并且开头下降要求原序列的第二个数比j小)且开头上升的抖动序列的方案一一对应。然而这里的开头上升怎么算呢?其实我们发现把一个长度为n、开头下降的序列a[i]的每一位转变为n-a[i]+1,它就变成了一个长度为n、开头上升的抖动序列。所以这时的方案数就=f[i-1][i-j];

  所以就可以愉快的dp辣!

代码:

技术分享
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
using namespace std;
int f[2][4210]={0};
int n,mod;
int main()
{
    int i,j;
    scanf("%d%d",&n,&mod);
    f[1][1]=1;
    for(i=2;i<=n;i++)
        for(j=1;j<=i;j++)
            f[i&1][j]=(f[i&1][j-1]+f[(i&1)^1][i-j])%mod;
    printf("%d\n",f[n&1][n]*2%mod);
}
短得让人心头一震

 

以上是关于bzoj1925地精部落[SDOI2010](dp)的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ1925[Sdoi2010]地精部落 组合数+DP

bzoj 1925 [Sdoi2010]地精部落(DP)

bzoj1925 [[Sdoi2010] 地精部落DP

BZOJ1925: [Sdoi2010]地精部落 DP+滚动数组

bzoj1925: [Sdoi2010]地精部落 [dp]

bzoj1925 [Sdoi2010]地精部落