zoj 3688 The Review Plan II 组合数学

我的解法比较慢。 复杂度 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;
}



你可能感兴趣的:(zoj 3688 The Review Plan II 组合数学)