Jzoj5235 好的排列
对于一个1->n的排列 ,定义A中的一个位置i是好的,当且仅当Ai-1>Ai 或者Ai+1>Ai。对于一个排列A,假如有不少于k个位置是好的,那么称A是一个好的排列。
现在有q个询问,每个询问给定n,k,问有多少排列是好的。答案对10^9+7取模。
显然是计数类dp,我们设f[i][j]表示对于一个1->i的排列,好的位置有j个的情况
考虑转移,显然f[i][j]->f[i+1][k]相当于插入一个i+1
那么我们考虑对j的影响,显然f[i][j]只能转移到f[i+1][j]或者f[i][j],因为这取决于你将i+1放在哪个位置上
如果放在一个不是好位置的两边,那么j就会+1,否则j不变,而且显然,不好的位置一定不连续(显然)
那么转移就十分简单了,参考code
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define M 1000000007
#define L long long
using namespace std;
void ad(int& x,L y){ x=(y+x)%M; }
int f[3010][3010]={0},s[3010][3010]={0},n;
int main(){
freopen("permutation.in","r",stdin);
freopen("permutation.out","w",stdout);
f[1][0]=1; f[2][1]=2;
f[3][1]=2; f[3][2]=4;
for(int i=3;i<3000;++i){
L pi=(i+1)-2,pj=2;
for(int j=i-1;pi>=0;--j){
ad(f[i+1][j],f[i][j]*pi);
ad(f[i+1][j+1],f[i][j]*pj);
pi-=2; pj+=2;
}
}
for(int i=1;i<=3000;++i)
for(int j=i-1;~j;--j){
s[i][j]=s[i][j+1];
ad(s[i][j],f[i][j]);
}
scanf("%d",&n);
for(int x,y,i=0;i<n;++i){
scanf("%d%d",&x,&y);
printf("%d\n",s[x][y]);
}
}