1. 程式人生 > >bzoj 3622 已經沒有什麽好害怕的了——二項式反演

bzoj 3622 已經沒有什麽好害怕的了——二項式反演

sca include ini space 一段 efi int 方案 ans

題目:https://www.lydsy.com/JudgeOnline/problem.php?id=3622

令 f[i] 表示欽定 i 對 a[ ]>b[ ] 的關系的方案數;g[i] 表示恰好 i 對 a[ ]>b[ ] 的關系的方案數。

那麽 \(f[i]=\sum\limits_{j>=i}C_{j}^{i}*g[j] \) ,\(g[i]=\sum\limits_{j>=i}C_{j}^{i}f[j](-1)^{j-i} \)

考慮怎麽求 f[ ] 。可以 DP 。

先把 a[ ] 和 b[ ] 都按從小到大的順序排序,dp[i][j]表示前 i 個 a[ ] 匹配了 j 對 a[ ] > b[ ] 的關系的方案數。

排序的好處就是 a[ ] > b[ ] 的一段 b[ ] ,a[i] 的這一段能包含 a[i-1] 的這一段。所以轉移就是 dp[i][j]=dp[i-1][j]+dp[i-1][j-1]*(p0-(j-1)),其中p0是比 a[i] 小的 b[ ] 的個數。

然後別忘了 f[ i ] = dp[n][i]*(n-i)! ,階乘表示其他配對可以隨意。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
int Mn(int
a,int b){return a<b?a:b;} const int N=2005,mod=1e9+9; int upt(int x){if(x>=mod)x-=mod;return x;} int n,k,a[N],b[N],dp[N],c[N][N]; void init() { for(int i=0;i<=n;i++)c[i][0]=1; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) c[i][j]=upt(c[i-1][j]+c[i-1][j-1]); } int
main() { scanf("%d%d",&n,&k); for(int i=1;i<=n;i++)scanf("%d",&a[i]); for(int i=1;i<=n;i++)scanf("%d",&b[i]); k+=n;if(k&1){puts("0");return 0;}// k>>=1; sort(a+1,a+n+1); sort(b+1,b+n+1); int p0=0;dp[0]=1; for(int i=1;i<=n;i++) { while(p0<n&&b[p0+1]<a[i])p0++; for(int j=Mn(i,p0);j;j--) dp[j]=(dp[j]+(ll)dp[j-1]*(p0-j+1))%mod; } for(int i=n,lj=1,j=0;i;i--,j++,lj=(ll)lj*j%mod) dp[i]=(ll)dp[i]*lj%mod; int ans=0; init(); for(int i=k,j=1;i<=n;i++,j=-j) ans=(ans+(ll)dp[i]*j*c[i][k])%mod; if(ans<0)ans+=mod; printf("%d\n",ans); return 0; }

bzoj 3622 已經沒有什麽好害怕的了——二項式反演