[bzoj3622]已經沒有什麽好害怕的了——容斥or二項式反演+DP
阿新 • • 發佈:2019-01-05
至少 表示 lld ans 配方 什麽 bug clas getchar
由於排了序的緣故,第\(i\)個數做出的決策一定有\(c_i-j+1\)種。
當然,最後\(f_{n,i}\)還需要乘以\((n-i)!\)。
這時我們可以得到最後的答案\(\{g\}\)與\(f_{n,i}\)的關系為:
\[ f_{i}=\sum_{j=i}^{n}{j \choose i}\times g_{j}\\]
根據二項式反演可得:
\[ g_{i}=\sum_{j=i}^{n}(-1)^{j-i}\times {j\choose i}\times f_j \]
直接\(O(n)\)反演即可。
當然觀察到\(f_i\)和\(g_i\)的關系之後,我們可以倒推利用容斥來推出\(g_i\) ,此時可得:
\[ g_i=f_i-\sum_{j=i+1}^{n}{j\choose i}g_j \]
這也不失為一種很好的理解方法。
題目大意:
給定兩個長度為\(n\)的序列,求有多少種匹配方式,使得\(a_i<b_i\)的個數恰好為\(k\)個。
思路:
據說是一道二項式反演的經典例題了。
首先如果要求正好等於\(k\)個的是不太好求的,我們可以考慮求出至少為\(k\)個的方案數。
首先先把兩個序列都按照從小到大的順序排好序,然後以序列\(b\)為對象dp。
我們設\(f_{i,j}\)表示前\(i\)個數裏面強制確定了\(j\)個\(a_i<b_i\)關系的方案數,記\(c_i\)表示在\(a\)中有多少個數<\(b_i\),於是可以得到方程
\[
f_{i,j}=f_{i-1,j}+f_{i-1,j-1}\times (c_{i}-j+1)\\]
由於排了序的緣故,第\(i\)個數做出的決策一定有\(c_i-j+1\)種。
當然,最後\(f_{n,i}\)還需要乘以\((n-i)!\)。
這時我們可以得到最後的答案\(\{g\}\)與\(f_{n,i}\)的關系為:
\[ f_{i}=\sum_{j=i}^{n}{j \choose i}\times g_{j}\\]
根據二項式反演可得:
\[ g_{i}=\sum_{j=i}^{n}(-1)^{j-i}\times {j\choose i}\times f_j \]
直接\(O(n)\)反演即可。
當然觀察到\(f_i\)和\(g_i\)的關系之後,我們可以倒推利用容斥來推出\(g_i\)
\[ g_i=f_i-\sum_{j=i+1}^{n}{j\choose i}g_j \]
這也不失為一種很好的理解方法。
#include<bits/stdc++.h> #define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i) #define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i) #define debug(x) cout<<#x<<"="<<x<<" " #define fi first #define se second #define mk make_pair #define pb push_back typedef long long ll; using namespace std; void File(){ freopen("bzoj3622.in","r",stdin); freopen("bzoj3622.out","w",stdout); } template<typename T>void read(T &_){ _=0; T f=1; char c=getchar(); for(;!isdigit(c);c=getchar())if(c=='-')f=-1; for(;isdigit(c);c=getchar())_=(_<<1)+(_<<3)+(c^'0'); _*=f; } const ll mod=1e9+9; const int maxn=2000+10; int n,m,a[maxn],b[maxn]; ll fac[maxn],ifac[maxn],c[maxn],f[maxn][maxn],g[maxn],ans; ll qpow(ll x,ll y){ ll ret=1; x%=mod; while(y){ if(y&1)ret=ret*x%mod; x=x*x%mod; y>>=1; } return ret; } void ad(ll &_,ll __){_=(_+__)%mod;} ll C(int x,int y){ return fac[x]*ifac[y]%mod*ifac[x-y]%mod; } int main(){ File(); read(n),read(m); REP(i,1,n)read(a[i]); REP(i,1,n)read(b[i]); if((n-m)%2)return puts("0"),0; m=(n-m)/2; fac[0]=1; REP(i,1,n)fac[i]=fac[i-1]*i%mod; ifac[n]=qpow(fac[n],mod-2); DREP(i,n-1,0)ifac[i]=ifac[i+1]*(i+1)%mod; sort(a+1,a+n+1); sort(b+1,b+n+1); int p=0; REP(i,1,n){ while(p<n && a[p+1]<b[i])++p; c[i]=p; } f[0][0]=1; REP(i,1,n)REP(j,0,i){ ad(f[i][j],f[i-1][j]); if(j)ad(f[i][j],f[i-1][j-1]*(c[i]-j+1)%mod); } REP(i,0,n)f[n][i]=f[n][i]*fac[n-i]%mod; /*REP(i,m,n)ad(ans,((i-m)%2 ? -1 : 1)*C(i,m)*f[n][i]%mod); printf("%lld\n",(ans+mod)%mod);*/ DREP(i,n,m){ g[i]=f[n][i]; REP(j,i+1,n)ad(g[i],-C(j,i)*g[j]%mod); } printf("%lld\n",(g[m]+mod)%mod); return 0; }
[bzoj3622]已經沒有什麽好害怕的了——容斥or二項式反演+DP