A:直接写降序的即可,剩下全部升序
B:水模拟
C:排序+贪心即可
D:先弄一个初始排列a[i]=i,然后枚举排列使其满足要求,可以用stl的next_permutation函数,很方便。然后将a[i]=i这种排列对应的方案数*n!(1~n的排列为n!种,b数组随a对应改变即可)即可,n<=16打表即可
E:递推好题!
|p[i]-i|=1,意味着第i位置要是cool位置的话只能取i+1或者i-1,这点很重要。递推前两维很容易想,f[i][j]表示前i个位置有j个位置是cool位置,但是只有两维我们显然找不到递推公式。由于前面的重要结论,我们可以想到第三维,f[i][j][k]表示前i个位置有j个位置是cool位置,k是两个二进制位,表示第i+1和i是否已经被选,由此可以建立递推关系。然后F(i) = Σ(f(n, i, k)) * (n - i)!但是注意,F[i]并不是仅仅i个cool位置的方案数,因为(n-i)!种包括了已经
有了i个cool位置的所有情况,显然,还可能包括更多的cool位置。注意到,F[n]必然是只有n个cool位置的方案数,我们可
以得到F[n-1],F[n-2]...。可以这样想,F[i],F[j](i<j)的关系。F[i]中重复计算了c[j][i]次f[j](c[j][i]为组
合数),(想一想,为什么)。减去重复计算的即可。
至于具体递推,详见代码:
#include<cstdio>//DP f[i][j][k]表示前i个位置选了j个好位置k是两个二进制位表示i+1,i是否用过
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=1005;
long long c[N][N],f[N][N][4],F[N];
const int MOD=1000000007;
void add(long long &x,long long y)
{
x=x+y;
if(x>=MOD) x=x-MOD;
}
int main()
{
int i,j,k,n,K;
scanf("%d%d",&n,&K);
for(i=0;i<=n;++i)
c[i][0]=1;
for(i=1;i<=n;++i)
for(j=1;j<=i;++j)
c[i][j]=(c[i-1][j-1]+c[i-1][j])%MOD;
f[0][0][2]=1;
for(i=1;i<=n;++i)
for(j=0;j<i;++j)
for(k=0;k<=3;++k)
{
if(k&1) add(f[i][j+1][(k>>1)+2],f[i-1][j][k]);
if(i<n) add(f[i][j+1][k>>1],f[i-1][j][k]);
add(f[i][j][(k>>1)+2],f[i-1][j][k]);
}
for(j=0;j<=n;++j)
{
for(k=0;k<=3;++k)
add(F[j],f[n][j][k]);
for(k=1;k<=n-j;++k)
F[j]=F[j]*k%MOD;
}
for(i=n-1;i>=0;--i)
for(j=i+1;j<=n;++j)
add(F[i],MOD-c[j][i]*F[j]%MOD);
printf("%d",F[K]);
// system("pause");
return 0;
}