【CF285E】Positions in Permutations(动态规划,容斥)
【CF285E】Positions in Permutations(动态规划,容斥)
题面
题解
首先发现恰好很不好算,所以转成至少,这样子只需要确定完一部分数之后剩下随意补。
然后套一个二项式反演进行容斥就可以得到答案了。
考虑怎么算至少\(m\)个的贡献,
设\(f[i][j][S]\)表示当前填到了位置\(i\),一个有\(j\)个贡献,\(i\)和\(i+1\)的使用情况是\(S\)的方案数,每次枚举一下这个位置是填\(i+1\)还是\(i-1\)还是其他就可以进行转移了。
#include<iostream>
#include<cstdio>
using namespace std;
#define MOD 1000000007
#define MAX 1010
void add(int &x,int y){x+=y;if(x>=MOD)x-=MOD;}
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int n,m,jc[MAX],a[MAX],ans;
int f[MAX][MAX][4],C[MAX][MAX];
int main()
{
n=read();m=read();
jc[0]=1;for(int i=1;i<=n;++i)jc[i]=1ll*jc[i-1]*i%MOD;
for(int i=0;i<=n;++i)C[i][0]=1;
for(int i=1;i<=n;++i)
for(int j=1;j<=i;++j)
C[i][j]=(C[i-1][j]+C[i-1][j-1])%MOD;
f[0][0][1]=1;
for(int i=1;i<=n;++i)
for(int j=0;j<i;++j)
for(int S=0;S<4;++S)
{
if(!f[i-1][j][S])continue;
add(f[i][j][S>>1],f[i-1][j][S]);
if(!(S&1))add(f[i][j+1][S>>1],f[i-1][j][S]);
if(i!=n)add(f[i][j+1][(S>>1)|2],f[i-1][j][S]);
}
for(int i=0;i<=n;++i)
for(int S=0;S<4;++S)
add(a[i],1ll*f[n][i][S]*jc[n-i]%MOD);
for(int i=m,d=1;i<=n;++i,d=MOD-d)add(ans,1ll*d*C[i][m]%MOD*a[i]%MOD);
printf("%d\n",ans);
return 0;
}