[NOIP模拟赛]排列问题

来源:互联网 发布:刘姓取名知乎 编辑:程序博客网 时间:2024/06/04 23:32
题目描述
长度为N的排列是一个序列(a1, a2, ..., an),恰好包含从1到N的每一个数字。例如,(3, 1, 4, 5, 2)是一个长度为5的排列。
对于两个排列a和b,定义magic(a, b) = max(a1, b1) + max(a2, b2) + ... + max(an, bn)

给定整数N和K,求有多少对排列a和b 满足a和b的长度都为N,并且magic(a, b) ≥ K。


输入格式

第1行:2个整数N(N≤50)和K(K≤2500)


输出格式

第1行:1个整数,表示答案,答案模1,000,000,007


输入样例

2 4


输出格式

2


样例说明
2个长度为2的排列一共有4种情况:
magic( (1,2), (1,2) ) = 1+2 = 3
magic( (1,2), (2,1) ) = 2+2 = 4
magic( (2,1), (1,2) ) = 2+2 = 4
magic( (2,1), (2,1) ) = 2+1 = 3
magic值大于等于4的有2对排列。



题解

设a排列:|   |   |   |   |   |,

设b排列:|   |   |   |   |   |,上下对应为一组格子

设dp[ful][haf][s]表示格子中有ful组全满, 2*haf组半满, 值为s时的总方案数


以n=5为例,

假设已有下面的状态:

|   | 5 | 4 |   |   |

|   |   | 5 | 4 |   |,现在要将数字3填进去,一共有4种转移方式:


①占2组半空格子:

|   | 5 | 4 | 3 |   |

|   | 3 | 5 | 4 |   |

a列有haf个空位可供选择,b列有haf个空位可供选择,对方案数的贡献为haf*haf,由于数是从大到小枚举,s值不变;


②占1组半空格子,1组全空格子:

|   | 5 | 4 | 3 |   |

| 3 |   | 5 | 4 |   |

a列有haf个空位可供选择,b列有emp个空位可供选择,当然也可以a列选emp个,b列选haf个,对方案数的贡献为haf*emp*2,因为填了一个全空格子,s值增大3;


③占2组全空格子,不错开

| 3 | 5 | 4 |   |   |

| 3 |   | 5 | 4 |   |

a列有emp个空位可供选择,b列有emp个空位可供选择,对方案数的贡献为emp*emp,因为填了1个全空格子,s值增大3;


④占2组全空格子,错开

| 3 | 5 | 4 |   |   |

|   |   | 5 | 4 | 3 |

a列有emp个空位可供选择,因为要错开,b列有emp-1个空位可供选择,对方案数的贡献为emp*(emp-1),因为填了2个全空格子,s值增大3*2;


#include<cstdio>const int Mod=1e9+7;const int N=55;int n, maxs, k, dp[N][N][N*N], sum;int main() {scanf( "%d%d", &n, &k ); maxs=n*n;dp[0][0][0]=1;for( int num=n; num>=1; num-- ) {//从大到小枚举int cnt=n-num;//已经填了cnt个数for( int ful=0; ful<=cnt; ful++ ) {//ful组格子全满int haf=cnt-ful, emp=n-ful-haf*2;//2*half组格子半满, emp组格子全空for( int s=0; s<=maxs; s++ ) {if( !dp[ful][haf][s] ) continue;if( haf )//占2组半空格子( dp[ful+2][haf-1][s]+=1ll*dp[ful][haf][s]*haf*haf%Mod )%=Mod;if( emp ) {//占1组半空格子, 1组全空格子( dp[ful+1][haf][s+num]+=1ll*dp[ful][haf][s]*emp*haf*2%Mod )%=Mod;//占2组全空格子(不错开)( dp[ful+1][haf][s+num]+=1ll*dp[ful][haf][s]*emp%Mod )%=Mod;if( emp>=2 )//占2组全空格子(错开)( dp[ful][haf+1][s+2*num]+=1ll*dp[ful][haf][s]*emp*(emp-1)%Mod )%=Mod;}}}}for( int i=k; i<=maxs; i++ ) sum=( sum+dp[n][0][i] )%Mod;printf( "%d\n", sum );return 0;}