簡單來說就是要問滿足a_1+...+a_n=s的n元組(a_1,...,a_n)的個數,並且a_i\leq f_i。記「滿足a_i>f_i且總和為s的n元組集合」為A_i,那麼所求就會是「總和為s的n元組」個數扣掉|A_1\bigcup ... \bigcup A_n|,因此可以考慮用排容原理。首先看所求的第一項,直接用排列組合的公式可以得到等於C_{n-1}^{n+s-1},這東西直接算就可以了。接下來要考慮A_i的部份,我們想要知道好幾個A_i交集起來的大小為多少。假設現在我們要算|A_{c_1}\bigcap ... \bigcap A_{c_r}|,那麼就是對於每個i,都有a_{c_i}>f_{c_i},因此我們先把s分給a_{c_1},...,a_{c_r}各f_{c_1}+1,...,f_{c_r}+1個,剩下再用排列組合的公式算就可以了。因此排容的過程就可以用個 DFS 來做,詳細可以參考 code 比較清楚。
code :
#include<bits/stdc++.h> #define LL long long #define MOD 1000000007 using namespace std; const int maxn=20 ; LL power(LL x,int n) { if(n<=1) return n ? x : 1 ; LL t=power(x,n/2) ; if(n%2) return (t*t%MOD)*x%MOD ; else return t*t%MOD ; } LL inv(int x) { return power(x,MOD-2) ; } LL fac[maxn],ifac[maxn] ; LL C(LL n,int m) { n%=MOD ; if(n<m) return 0 ; LL ret=1 ; for(int i=0;i<m;i++) ret=ret*(n-i)%MOD ; return ret*ifac[m]%MOD ; } LL a[maxn],ans ; int n ; void dfs(int x,LL s,int mul) { if(x==n){ans=(ans+mul*C(s+n-1,n-1))%MOD ; return ;} dfs(x+1,s,mul) ; if(s>a[x]) dfs(x+1,s-a[x]-1,-mul) ; } main() { for(int i=0;i<maxn;i++) fac[i]= (i?fac[i-1]*i%MOD : 1) , ifac[i]=inv(fac[i]) ; LL s ; scanf("%d%lld",&n,&s) ; for(int i=0;i<n;i++) scanf("%lld",&a[i]) ; dfs(0,s,1) ; printf("%lld\n",(ans+MOD)%MOD) ; }
沒有留言:
張貼留言