Codeforces Round #178 (Div. 2) C. Shaass and Lights 【组合数学】

http://codeforces.com/contest/294/problem/C

先考虑连续子集内的情况,如果有两边的情况,ans *=1 ;不然,设子集大小为n,ans*=2^(n-1)。

在自己间的组合问题,设每个子集大小为Ni(1<=i<=m),ans = ans * C(N1+...+Nm,N1) * C(N2+...+Nm,N2) * ... * C(N(m-1)+Nm,N(m-1)) * C(Nm,Nm)。

注意n == m的时候ans=1,因为空集也是一种情况。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#include <cmath>
#include <queue>
using namespace std;
template <class T> void checkmin(T &t,T x) {if(x < t) t = x;}
template <class T> void checkmax(T &t,T x) {if(x > t) t = x;}
template <class T> void _checkmin(T &t,T x) {if(t==-1) t = x; if(x < t) t = x;}
template <class T> void _checkmax(T &t,T x) {if(t==-1) t = x; if(x > t) t = x;}
typedef pair <int,int> PII;
typedef pair <double,double> PDD;
typedef long long ll;
#define foreach(it,v) for(__typeof((v).begin()) it = (v).begin(); it != (v).end ; it ++)
#define MOD 1000000007
ll mi(ll a , ll b) {
    if(b == 0) return 1;
    if(b == 1) return a % MOD;
    ll t = 1;
    if(b % 2 == 1) t = a;
    ll tt = mi(a , b/2);
    return ( ( tt * tt ) % MOD * t ) % MOD;
}
ll jc(ll a) {
    ll b = a;
    ll ans = 1;
    while(b > 1) {
        ans = (ans * b) % MOD;
        b = b - 1;
    }
    return ans;
}
void gcd(ll a , ll b , ll &d , ll &x , ll &y) {
    if(!b) {d = a; x = 1; y = 0;}
    else { gcd(b , a%b,d,y , x); y -= x * (a/b); }
}
ll inv(ll a , ll n) {
    ll d , x , y;
    gcd(a , n , d,  x , y);
    return d == 1 ? (x+n)%n : -1;
}
ll a[10100];
ll  b[10100];
int n , m , aa;
int cnt = 0;
int main() {
    cin >> n >> m;
    for(int i=0;i<m;i++) cin >> a[i];
    if(n == m) { cout << "1" << endl; return 0; }
    sort(a,a+m);
    if(a[0] != 1) b[cnt++] = a[0] - 1;
    for(int i=1;i<m;i++) {
        ll del = a[i] - a[i-1] -1;
        if(del <= 0) continue;
        b[cnt++] = del;
    }
    if(a[m-1] != n) b[cnt++] = n - a[m-1];
    ll ans = 1;
    int start = 1;
    if(a[0] == 1) start = 0;
    int end = cnt-1;
    if(a[m-1] == n) end = cnt;
    for(int i=start;i<end;i++) ans = (ans * mi(2,b[i]-1)) % MOD;
    ll mm = n - m;
    for(int i=0;i<cnt-1;i++) {
        ans = (ans * jc(mm) ) % MOD;
        ll ggg = inv( jc(b[i]) , MOD );
        ans = (ans * ggg) % MOD;
        ggg = inv( jc(mm - b[i]) , MOD );
        ans = (ans * ggg) % MOD;
        mm -= b[i];
    }
    cout << ans << endl;
    return 0;
}

 求逆部分当时用的是刘汝佳老师《算法竞赛入门经典训练指南》上的模板

posted @ 2013-04-08 02:06  aiiYuu  阅读(318)  评论(0编辑  收藏  举报