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; }
求逆部分当时用的是刘汝佳老师《算法竞赛入门经典训练指南》上的模板