CF285E Positions in Permutations

我这里和别人不一样的是取模加和乘(我觉得这个东西很好,可能会快一点?

Solution

首先,它让我们算方案数,所以我们设一个数组 \(F(x)\) 表示完美数恰好为 \(x\) 的排列数,又由这熟悉的恰好,可以再设一个数组 \(G(x)\) 表示完美数至少\(x\) 的排列数,那么显然可以得到: \(G(m)=\sum\limits_{i=m}^n\binom imF(i)\) ,来一个正常的二项式反演就可以得到 \(F(m)=\sum\limits_{i=m}^n(-1)^{i-m}\binom imG(i)\) ,现在考虑如何求出 \(G(x)\)

因为每一位的完美情况只和前后有关系,所以我们可以设 \(f_{i,j,0/1,0/1}\) 来表示当前位是 \(i\) ,有 \(j\) 个完美数, \(i\) 是/否被选, \(i+1\) 是/否被选的方案数。

现在考虑状态转移方程:

选择第 \(i\) 位为完美位,并让 \(i-1\) 在第 \(i\) 位,只需要保证 \(i-1\) 一定没被选。

\[f_{i,j,0,0}+=f_{i-1,j-1,0,0},f_{i,j,1,0}+=f_{i-1,j-1,0,1} \]

选择第 \(i\) 位为完美位,并让 \(i+1\) 在第 \(i\) 位,只需要保证 \(i+1\) 一定没被选。

\[f_{i,j,0,1}+=f_{i-1,j-1,0,0}+f_{i-1,j-1,1,0},f_{i,j,1,1}+=f_{i-1,j-1,0,1}+f_{i-1,j-1,1,1} \]

不选择第 \(i\) 位为完美位

\[f_{i,j,0,0}+=f_{i-1,j,0,0}+f_{i-1,j,1,0},f_{i,j,1,0}+=f_{i-1,j,0,1}+f_{i-1,j,1,1} \]

初始状态: \(f_{1,0,0,0}=1,f_{1,1,0,1}=1\)

因为第 \(n\) 位只能选择 \(n-1\) 为完美数,所以需要重新转移或者减去 \(n+1\) 的部分。我选择的重新转移。

现在就能得到 \(G(m)=(f_{n,m,0,0}+f_{n,m,1,0})\times(n-m)!\) ,其中 \((n-m)!\) 是剩下 \(n-m\) 个位置随便排的全排列。

最后反演即可。

代码

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long

using namespace std;
const int N=2050,mod=1e9+7;
int n,m;
ll f[N][N][2][2],G[N],fac[N],inv[N];

inline int read(){
    int x=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
    return x*f;
}

ll mul(ll a,ll b){return a*b%mod;}
ll add(ll a,ll b){return a+b>=mod?a+b-mod:a+b;}

inline void init(){
    fac[0]=inv[0]=inv[1]=1;
    for(int i=2;i<=n;i++)
        inv[i]=mul(inv[mod%i],mod-mod/i);
    for(int i=1;i<=n;i++){
        fac[i]=mul(fac[i-1],i);
        inv[i]=mul(inv[i],inv[i-1]);
    }
}

ll C(int n,int m){
    if(n<m) return 0;
    return mul(fac[n],mul(inv[n-m],inv[m]));
}

int main(){
    n=read();m=read();
    init();
    f[1][0][0][0]=f[1][1][0][1]=1;
    for(int i=2;i<=n;i++){
        f[i][0][0][0]=1;
        for(int j=1;j<=i;j++){
            f[i][j][0][0]=add(f[i-1][j-1][0][0],add(f[i-1][j][0][0],f[i-1][j][1][0]));
            f[i][j][0][1]=add(f[i-1][j-1][0][0],f[i-1][j-1][1][0]);
            f[i][j][1][0]=add(f[i-1][j-1][0][1],add(f[i-1][j][1][1],f[i-1][j][0][1]));
            f[i][j][1][1]=add(f[i-1][j-1][0][1],f[i-1][j-1][1][1]);
        }
    }
    f[n][0][0][0]=1;
    for(int i=1;i<=n;i++){
        f[n][i][0][0]=add(f[n-1][i-1][0][0],add(f[n-1][i][0][0],f[n-1][i][1][0]));
        f[n][i][1][0]=add(f[n-1][i-1][0][1],add(f[n-1][i][0][1],f[n-1][i][1][1]));
    }
    //上面是重新转移的n
    for(int i=0;i<=n;i++)
        G[i]=mul(add(f[n][i][0][0],f[n][i][1][0]),fac[n-i]);
    ll ans=0;
    for(int i=m;i<=n;i++){
        ll res=mul(C(i,m),G[i]);
        ans=add(ans,(i-m)&1?mod-res:res);
    }
    printf("%lld\n",ans);
    return 0;
}
posted @ 2020-10-09 08:25  jasony_sam  阅读(88)  评论(0编辑  收藏  举报