题意:
如果有2个排列a,b,定义序列c为:
c[i] = (a[i] + b[i] - 2) % n + 1
但是,明显c不一定是一个排列
现在,给出排列的长度n (1 <= n <= 16)
问有多少种a,b的排列的组合的方案,使得得到的c也是一个排列
排列的组合a = x,b = y 与 排列的组合a = y,b = x算是2种方案
思路:
有3个排列,不妨假设排列a 为1,2,3,...,n
这样结果再 * n! 就是答案
考虑状压dp
j是一个16位的数,记录b的n个数被用了哪些数
f(i,j)表示a用了1~i,b用了j记录的那些数,产生的c的前i个的方案数
init : f(0,0) = 1
ans = f(n,(1<<n)-1) * n!转移方程:
没法转移阿,因为我们还必须知道c哪些数用了,哪些数没有用
如果再加一维记录c的状态的话,数组开不下了
那就写成搜索的形式了
dfs(u,v,w) 表示a用了1~u,b,c分别用了v,w记录的那些数
if(u == n + 1) ans++;
但是这样会超时啊,1 <= n <= 16,这么小,那就直接打表了
代码:
//File Name: cf285D.cpp //Author: long //Mail: [email protected] //Created Time: 2016年07月09日 星期六 16时44分05秒 #include <stdio.h> #include <string.h> #include <algorithm> #include <iostream> #define LL long long using namespace std; const int MOD = (int)1e9 + 7; LL ans,n; LL jie[17]; int res[17] = {0,1,0,18,0,1800,0,670320,0,734832000,0,890786230,0,695720788,0,150347555,0}; void init(){ jie[0] = 1; for(int i=1;i<17;i++) jie[i] = i * jie[i-1] % MOD; //ans = 0; //dfs(1,0,0); } void dfs(int a,int b,int c){ if(a == n + 1){ ans++; if(ans >= MOD) ans -= MOD; return ; } int v; for(int i=0;i<n;i++){ if(((1<<i) & b) == 0){ v = (i + a - 1) % n; if(((1<<v) & c) == 0) dfs(a+1,b|(1<<i),c|(1<<v)); } } } int main(){ init(); while(~scanf("%d",&n)){ printf("%d\n",res[n]); } return 0; }
时间: 2024-09-26 20:09:25