【BZOJ 3622】3622: 已经没有什么好害怕的了(DP+容斥原理)
3622: 已经没有什么好害怕的了
Time Limit: 10 Sec Memory Limit: 256 MB
Submit: 683 Solved: 328Description
Input
Output
Sample Input
4 2
5 35 15 45
40 20 10 30
Sample Output
4HINT
输入的2*n个数字保证全不相同。
还有输入应该是第二行是糖果,第三行是药片Source
【分析】
もう何も怖くない
首先n+k为奇特判无解。
然后知道要选多少组ai>bj的
然后就选吧。
先两个都排一遍序
$f[i][j]$表示选了a的前$i$组后有$j$组$ai>bj$的,的方案数。
这个很简单好吗!!(我昨天晚上看这题时候绝对脑抽)
$f[i][j]=f[i-1][j]+f[i-1][j-1]*(mx[i]-j+1)$ 剩下的先不用管,后面乱排乘一个阶乘
$mx[i]$表示最多前$mx[i]$个$b$数组小于$a[i]$
但是!!不能保证刚好就等于你要的组数,可能大于它。于是容斥大法就来了。
$dp[i]=f[n][i]*(n-i)!-dp[j]*C[j][i] (j>i)$
组合数也递推就好了。
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 using namespace std; 7 #define Maxn 2010 8 #define Mod 1000000009 9 #define LL long long 10 11 LL mymax(LL x,LL y) {return x>y?x:y;} 12 13 LL a[Maxn],b[Maxn],f[Maxn][Maxn],mx[Maxn],dp[Maxn],p[Maxn]; 14 LL c[Maxn][Maxn]; 15 16 int main() 17 { 18 LL n,k; 19 scanf("%lld%lld",&n,&k); 20 if((n+k)&1) printf("0\n"); 21 else 22 { 23 k=(n+k)/2; 24 for(LL i=1;i<=n;i++) scanf("%lld",&a[i]); 25 for(LL i=1;i<=n;i++) scanf("%lld",&b[i]); 26 sort(a+1,a+1+n); 27 sort(b+1,b+1+n); 28 LL st=0; 29 for(LL i=1;i<=n;i++) 30 { 31 while(a[i]>b[st+1]&&st<n) st++; 32 mx[i]=st; 33 } 34 memset(f,0,sizeof(f)); 35 for(int i=0;i<=n;i++) f[i][0]=1; 36 for(LL i=1;i<=n;i++) 37 for(LL j=1;j<=i;j++) 38 { 39 f[i][j]=(f[i-1][j]+f[i-1][j-1]*mymax(mx[i]-j+1,0))%Mod; 40 } 41 for(LL i=0;i<=n;i++) c[i][0]=1; 42 for(LL i=1;i<=n;i++) 43 for(LL j=1;j<=i;j++) 44 { 45 c[i][j]=(c[i-1][j]+c[i-1][j-1])%Mod; 46 } 47 p[0]=1; 48 for(LL i=1;i<=n;i++) p[i]=(p[i-1]*i)%Mod; 49 for(LL i=n;i>=k;i--) 50 { 51 dp[i]=(f[n][i]*p[n-i])%Mod; 52 for(LL j=i+1;j<=n;j++) 53 { 54 dp[i]=dp[i]-c[j][i]*dp[j]; 55 dp[i]=(dp[i]%Mod+Mod)%Mod; 56 } 57 } 58 printf("%lld\n",dp[k]); 59 } 60 return 0; 61 }
もう何も怖くない(呵呵
2017-04-06 14:16:58