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

posted @ 2017-11-16 19:32  扩展的灰(Extended_Ash)  阅读(117)  评论(0编辑  收藏  举报