[SDOI2010] 地精部落 (DP+组合)

一句话题意

给定1到n求其全排列中没有三个及以上递增(减)的方案数,ans%p(p不一定为素数)

错误打法一

我看到这道题后首先想到的是容斥:

ans=全排列-至少3个不满足的+至少4个......

但是考虑不到有两个不连续的递增(减)的情况,所以WA0了。

错误打法二

f[i][0]代表前i个已经处理好了且下一个该是山谷的方案,1同理。

假如有一个这样的数列(i=8)

3 2 7 4  5 6 1

      j       

4 5 6不满足条件,所以要把8往4 5之间放,所以便有

f[i][(i-j-1)&1]+=f[j][1]*f[i-j-1][(i-j-1)&1];

但是我们会发现我们不能考虑到i放的地方前一个和后一个的大小所以这个又崩了......

正解

其实不用考虑往中间插,只要考虑往末尾放就行,即:

f[i][k&1]+=f[j][1]*f[k][k&1];(k=i-j-1)

呃...样例又没过。哦哦,忘记考虑i-1个元素的排列了。

最终式子:

f[i][k&1]+=c[i-1][j]*f[j][1]*f[k][k&1];(k=i-j-1)

因为p不保证是质数,所以组合用杨辉三角递推即可。

 

#include<iostream>
#include<cstdio>
#define ll long long
using namespace std;
ll ans,n,p,f[5000][2],a[5000][5000];
int main()
{
        scanf("%lld%lld",&n,&p);
        a[0][0]=1;
        for(int i=1;i<=n;i++)
        {
                a[i][0]=1;
                for(int j=1;j<=i+1;j++)
                {
                        a[i][j]=(a[i-1][j]+a[i-1][j-1])%p;
                }
        }
        f[0][0]=f[0][1]=f[1][1]=f[1][0]=1;
        for(int i=2;i<=n;i++)
        {
                for(int j=0;j<i;j++)
                {
                        ll k=i-j-1;
                        (f[i][k&1]+=a[i-1][j]*f[j][1]%p*f[k][k&1]%p)%=p;
                }
        }
        ans=(f[n][1]+f[n][0])%p;
        printf("%lld",ans);
        return 0;
}
View Code

 

posted @ 2019-07-02 12:06  ATHOSD  阅读(81)  评论(0编辑  收藏  举报