1. 程式人生 > >【BZOJ3622】已經沒有什麼好害怕的了 動態規劃+容斥原理

【BZOJ3622】已經沒有什麼好害怕的了 動態規劃+容斥原理

連結:

#include <stdio.h>
int main()
{
    puts("轉載請註明出處[vmurder]謝謝");
    puts("網址:blog.csdn.net/vmurder/article/details/44836095");
}

題解:

首先我們給A陣列(糖果)和B陣列(藥片)從小到大排個序。
lasti 表示一個極大值 x 使得 Bx<Ai
f(i,j) 表示列舉到第 Ai 時,有至少 j 對匹配,使得 A>B
然後列舉到 Ai 不代表也必須只能使用 Bi 以及其前的B陣列元素。

f(i,j)=f(i1,j

)+f(i1,j1)(lasti(j1))
其中 f(i1,j) 表示 Ai 匹配了一個比它大的 B
f(i1,j1)(lasti(j1)) 則表示匹配了一個比它小的,
而後面的乘數則是它有lasti種選擇,其中有 (j1) 個被用過了。

之後進行容斥原理,設 g(i,j) 表示列舉到第 Ai 時,有正好 j 對匹配,使得 A>B 的方案數。
我們發現對於一個 f(i,j) 它的非確定 A>B 的那些數對中,有 (ij)!種排列。
然後我們要從中減去不符合要求的,也就是其中有 A>B的那些,就得到了 g

(i,j)

那麼顯然式子應該是這樣的:
g(i,j)=f(i,j)(ij)!nk=j+1g(i,k)Cij

然後我們考慮應該有多少對 A>B

呃,設有 x 對吧,那麼就有 nxA>B
然後 x(nx)=k ,所以 x=n+k2 這裡需要特判一下,如果是非整數會出大新聞。

程式碼:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 2050
#define inf 0x3f3f3f3f
#define mod 1000000009 using namespace std; int a[N],b[N],n,m; long long fac[N],C[N][N],f[N][N]; int main() { freopen("test.in","r",stdin); int i,j,k; scanf("%d%d",&n,&m); if(n+m&1){puts("0");return 0;} m=n+m>>1; for(i=1;i<=n;i++)scanf("%d",&a[i]); for(i=1;i<=n;i++)scanf("%d",&b[i]); sort(a+1,a+n+1),sort(b+1,b+n+1); for(k=0,f[0][0]=i=1;i<=n;i++) { while(k<n&&b[k+1]<a[i])k++; for(f[i][0]=j=1;j<=i;j++) f[i][j]=(f[i-1][j]+f[i-1][j-1]*max(k-(j-1),0))%mod; } for(fac[0]=i=1;i<=n;i++)fac[i]=fac[i-1]*i%mod; for(i=0;i<=n;i++) { C[i][0]=1; for(j=1;j<=i;j++)C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod; } for(i=n;i>=m;i--) { f[n][i]=f[n][i]*fac[n-i]%mod; for(j=i+1;j<=n;j++) { f[n][i]-=f[n][j]*C[j][i]%mod; f[n][i]=(f[n][i]+mod)%mod; } } cout<<f[n][m]<<endl; return 0; }