我的解法比较慢。 复杂度 O(nlgn) 有O( N )的解法 递推公式看不懂
此题是 有 禁位 的排列 。相当于在 n*n的棋盘中 设置了 2n 个 禁位。
有 k个在 这 2*n 个禁位中 的排列数 Pk。 这 2*n 个 禁位 可以看成 一个有 t =2*n 个点 形成的环中 ,选出k个不相邻的组合数。
可以通过 枚举圆上的 点 就将 圆拆分成了链。
先选取一个点 有 t 种情况。 选了这个点之后 剩余 t-3 个点 ,形成一条链 。 从中选k-1 个不相邻的点, C(t- k-1 , k-1)
由于对于 每种组合 都 枚举够k 次。则 从 t 个点的环中 ,选k个不相邻的点的组合数是 Pk = t * C( t- k - 1, k -1 ) / k
最终的结果就是 P0 - p1 + p2 - P3 + P4 ......... 利用容斥原理求解
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <assert.h>
#include <queue>
#define REP(i,n) for(int i=0;i<n;i++)
#define TR(i,x) for(typeof(x.begin()) i=x.begin();i!=x.end();i++)
#define ALLL(x) x.begin(),x.end()
#define SORT(x) sort(ALLL(x))
#define CLEAR(x) memset(x,0,sizeof(x))
#define FILLL(x,c) memset(x,c,sizeof(x))
using namespace std;
const double EPS = 1e-8;
typedef long long LL;
typedef double db;
#define pb push_back
#define mp make_pair
const int mod = 1000000007;
LL pow(LL a,LL b){
LL ret =1;
while(b){
if(b&1){
ret *= a;
ret %= mod;
}
a =a *a;
a%= mod;
b = b>>1;
}
return ret;
}
const int maxn = 200100;
LL f[maxn];
void init(){
f[0] =1;
for(int i=1;i<=maxn;i++){
f[i] = i*f[i-1];
f[i]%= mod;
}
}
void solve(int n){
LL ans = 0;
int t = 2*n;
for(int k = 0 ;k<= n;k++){
LL up = t *f[t-k-1];
up%= mod;
LL down = f[k]*f[t- 2*k];
down%= mod;
down = pow(down , mod-2);
up = up *down;
up%= mod;
up*= f[n-k];
up%= mod;
if(k&1){
ans -= up;
ans = (ans + mod)%mod;
}else{
ans += up;
ans%= mod;
}
}
printf("%d\n",(int)ans);
}
int n;
int main(){
init();
while(~scanf("%d",&n)){
if(n==1){
cout << 0 <<endl;
continue;
}
solve(n);
}
return 0;
}
本文探讨了在解决复杂排列问题时,如何通过优化算法减少时间复杂度至O(N),并详细解释了如何将问题转化为在环形结构中选择不相邻元素的组合问题,以及如何运用容斥原理进行求解。

被折叠的 条评论
为什么被折叠?



