【BZOJ3622】已經沒有什么好害怕的了
Description
Input
Output
Sample Input
5 35 15 45
40 20 10 30
Sample Output
HINT
輸入的2*n個數字保證全不相同。
題解:好吧這題不是神題,而是套路題,容斥+DP的套路在很多題中都用到過,不過我雖然知道套路,卻被這題的第一步卡住了。
我們將兩個序列從小到大排序。
好吧這步看起來可能很水,正常人看到無序的序列都會先想到排序,然而為什么要兩個都從小到大排呢?排序的意義何在?一會講。
題中給的是(糖果>葯片)=(葯片>糖果)+k,我們只需要用一點小學數學的知識就能算出(糖果>葯片)的具體數量。下面是套路部分:我們設f[i][j]表示枚舉到前i個,(糖果>葯片)數至少為j的方案數。然后我們預處理出最大的k,滿足葯片k<糖果i,那么方程就是:
$f[i][j]=f[i-1][j]+f[i-1][j-1]*(k-j+1)$
現在知道排序的意義了吧?因為后面的i的決策區間一定包含前面的i的決策區間,所以只需要將其減去即可防止重復。
那么最后答案是什么呢?套路:ans=至少有k個的方案數-至少有k+1個的+至少有k+2個的方案數-。。。
所以答案=$\sum\limits_{j=k}^nf[n][j](-1)^{j-k}C_j^k$
P.S:這里解釋一下容斥系數是$C_j^k$的原理吧,首先我們用f[i]代表上面的f[n][i],用g[i]代表恰好有i個符合條件的方案數。根據定義有:$f[i]=\sum\limits_{j=i}^ng[i]C_j^i$。所以我們上面得到的最終式子可以轉化成:$\sum\limits_{j=k}^nf[j](-1)^{j-k}C_j^k=\sum\limits_{i=k}^ng[i]\sum\limits_{j=k}^i(-1)^{j-k}C_i^jC_j^k\\=\sum\limits_{i=k}^ng[i]\sum\limits_{j=k}^i(-1)^{j-k}C_i^kC_{i-k}^{j-k}\\=\sum\limits_{i=k}^ng[i]C_i^k\sum\limits_{j=k}^i(-1)^{j-k}C_{i-k}^{j-k}$
根據組合恆等式后面那個東西只在i=k的時候=1,其余時候=0,所以我們就得到了g[k]。
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; const ll P=1000000009; int n,m; ll ans; ll f[2010][2010],c[2010][2010],jc[2010]; int a[2010],b[2010]; inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<'0'||gc>'9') {if(gc=='-') f=-f; gc=getchar();} while(gc>='0'&&gc<='9') ret=ret*10+(gc^'0'),gc=getchar(); return ret*f; } int main() { n=rd(),m=rd(); if((n^m)&1) { printf("0"); return 0; } m=(n+m)>>1; int i,j,k; for(jc[0]=i=1;i<=n;i++) jc[i]=jc[i-1]*i%P; for(i=1;i<=n;i++) a[i]=rd(); for(i=1;i<=n;i++) b[i]=rd(); for(i=0;i<=n;i++) { c[i][0]=1; for(j=1;j<=i;j++) c[i][j]=(c[i-1][j-1]+c[i-1][j])%P; } sort(a+1,a+n+1),sort(b+1,b+n+1); f[0][0]=1; for(i=1;i<=n;i++) { for(k=1;k<=n&&b[k]<a[i];k++); k--; for(j=1;j<=i;j++) f[i][j]=(f[i-1][j]+f[i-1][j-1]*max(k-j+1,0))%P; f[i][0]=f[i-1][0]; } ll tmp=1; for(i=m;i<=n;i++) f[n][i]=(f[n][i]*jc[n-i])%P,ans=(ans+tmp*f[n][i]*c[i][m]%P+P)%P,tmp=-tmp; printf("%lld",(ans+P)%P); return 0; }