【杂题】【DP】【容斥原理】[Codeforces 285E] Positions in Permutations

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hzj1054689699/article/details/81988606

Description

原题传送门

定义一个排列P的第i个位置是好的,当且仅当 | P i i | = 1
给出n,m,要求长度为n的好的位置恰好有m个的排列数量。

1 <= n <= 1000 , 0 <= m <= n

Solution

既然是恰好m个,我们第一反应肯定是容斥。

求出至少有多少个好的位置。

考虑DP,(不得不说这个状态设计真是精妙)

我们先假定不考虑它具体是怎么排列的,按位置填数。
第i个位置是好的,那就要么它放了i+1,要么它放了i-1

F [ i ] [ j ] [ 0 / 1 ] [ 0 / 1 ] 表示当前放到第i个位置,已经确定有j个位置是好的,数i是否已用,数i+1是否已用。
枚举下一个是否让他成为好的位置,分情况讨论即可。
注意如果不是好的位置,我们管它具体选什么,直接将他当做任意的数来转移即可。

最后令 G [ i ] 表示至少有i个位置的答案,它明显等于 f [ n ] [ i ] [ 0 ] [ 0 ] + f [ n ] [ i ] [ 1 ] [ 0 ] (不存在n+1这个数)

总的答案就是 j = m n G [ j ] ( n j ) ! ( 1 ) j m

Code

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define N 1005
#define mo 1000000007
#define LL long long
using namespace std;
LL js[N],g[N],f[N][N][2][2],ny[N];
int n,m;
void add(LL &x,LL y)
{
    x=(x+y)%mo;
}
LL C(int n,int m)
{
    if(n<m) return 0;
    return js[n]*ny[m]%mo*ny[n-m]%mo;
}
int main()
{
    cin>>n>>m;
    f[0][0][1][0]=1;
    js[0]=1;
    fo(i,1,n) js[i]=js[i-1]*(LL)i%mo;
    fo(i,0,n-1)
    {
        fo(j,0,i)
        {
            add(f[i+1][j][0][0],f[i][j][1][0]+f[i][j][0][0]);
            add(f[i+1][j][1][0],f[i][j][0][1]+f[i][j][1][1]);

            add(f[i+1][j+1][0][0],f[i][j][0][0]);
            add(f[i+1][j+1][0][1],f[i][j][0][0]+f[i][j][1][0]);
            add(f[i+1][j+1][1][0],f[i][j][0][1]);
            add(f[i+1][j+1][1][1],f[i][j][0][1]+f[i][j][1][1]);
        }
    }
    fo(j,0,n) 
    {
        g[j]=(f[n][j][0][0]+f[n][j][1][0])%mo*js[n-j]%mo;
    }
    ny[0]=ny[1]=1;
    fo(i,2,n) ny[i]=(-ny[mo%i]*(LL)(mo/i)+mo)%mo;
    fo(i,1,n) ny[i]=ny[i-1]*ny[i]%mo;
    LL ans=0;
    LL v=1;
    fo(j,m,n)
    {
        ans=(ans+g[j]*C(j,m)%mo*v+mo)%mo;
        v=-v;
    }
    printf("%I64d\n",ans);
}

猜你喜欢

转载自blog.csdn.net/hzj1054689699/article/details/81988606