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;
}