BZOJ 1925地精部落题解

题目链接

一道神仙题,有很多思考的方式,这里选择最好理解的一种来讲

我们将序列分为两种,一种开头递增,一种开头递减,显然这两种序列的数目是一样的

现在我们只用考虑开头递增的情况

f[i][j]表示前i个数,最后一个数字在前i个数的排名在1~j之间的方案数

显然有f[i][j]=f[i][j-1],如果最后一个数下降f[i][j]可以由f[i-1][i-j]转移过来,

如果最后一个数上升,则可以由f[i-1][j-1]转移过来,考虑到每一次转移时没有考虑之前一次的状态最后是上升还是下降的

我们都转移过来会错,由于波动序列具有对称性且我们只考虑了开头递增的序列,所以我们只用从f[i-1][i-j]来转移就ok了

还有,记得加上滚动数组进行优化

# include<iostream>
# include<cstring>
# include<algorithm>
# include<cmath>
# include<cstdio>
using namespace std;
const int mn = 4205;
long long f[2][mn];
int n,p;
int main()
{
    scanf("%d%d",&n,&p);
    f[0][1]=1;
    if(n==1)
    {
        printf("1");
        return 0;
    }
    int cnt=0;
    for(int i=2;i<=n;i++)
    {
        cnt=1-cnt;
        for(int j=1;j<=i;j++)
            f[cnt][j]=(f[cnt][j-1]+f[1-cnt][i-j])%p;
    }
    printf("%lld",(f[cnt][n]*2)%p);
    return 0;
}

 

posted @ 2018-09-26 22:04 logeadd 阅读(...) 评论(...) 编辑 收藏