zoukankan      html  css  js  c++  java
  • BZOJ3622 已经没有什么好害怕的了 【dp + 二项式反演】

    题目链接

    BZOJ3622

    题解

    既已开题
    那就已经没有什么好害怕的了

    由题目中奇怪的条件我们可以特判掉(n - k)为奇数时答案为(0)
    否则我们要求的就是糖果大于药片恰好有(frac{n - k}{2} + k)个的方案数,我们记为(K)

    思路1##

    直接求恰好不好求,想到二项式反演:
    如果有

    [b_k = sumlimits_{i = k}^{n} {i choose k} a_i ]

    那么有

    [a_k = sumlimits_{i = k}^{n} (-1)^{i - k} {i choose k} b_i ]

    如果我们令(a_k)为恰好有(K)对糖果大于药片的方案数
    从式子来看,对于每一种(a_i)的情况,(b_k)都从中选出任意(k)
    那么(b_k)就是任选(k)个满足条件,剩余随便选的方案数

    问题就转化为了求出(b_k),显然可以(dp)
    我们先将糖果与药片分别升序排序
    我们设(f[i][j])表示前(i)个糖果选出(j)个和药片匹配,都大于药片的方案数
    我们记(pos_i)为第(i)个糖果比药片大的最大的位置,那么如果第(i)个糖果参与匹配,就有(pos_i)种选择
    由于糖果是升序排序,所以剩余(j - 1)个糖果选择范围一定不比(i)大,所以(i)实际剩余(pos_i - (j - 1))种选择
    那么有

    [f[i][j] = f[i - 1][j] + f[i - 1][j - 1] * (pos_i - j + 1) ]

    求出(f[n][i]),则(b_k = f[n][k] * (n - k)!)
    然后再用二项式反演即可求出(a_k)

    思路2##

    如果你不知道二项式反演,其实有一个更直观的方法
    同样是求出(f[n][i])后求出(b_i)
    我们令(ans[k])表示恰好(k)个糖果大于药片的方案数
    显然

    [ans[n] = b_n ]

    (k < n)时,(b_k)多算了恰好为(k + 1)(k + 2)(k + 3......)的方案数,减去即可

    [ans[k] = b_k - sumlimits_{i = k + 1}^{n} ans[i] ]

    这样也是对的

    PS##

    由思路2其实我们可以看到二项式反演实际上就是一个容斥的结果

    思路一

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #include<map>
    #define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define mp(a,b) make_pair<int,int>(a,b)
    #define cls(s) memset(s,0,sizeof(s))
    #define cp pair<int,int>
    #define LL long long int
    using namespace std;
    const int maxn = 2005,maxm = 100005,INF = 1000000000,P = 1000000009;
    inline int read(){
    	int out = 0,flag = 1; char c = getchar();
    	while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
    	while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
    	return out * flag;
    }
    LL f[maxn][maxn],C[maxn][maxn],fac[maxn];
    int a[maxn],b[maxn];
    int n,K;
    void init(){
    	fac[0] = 1;
    	for (int i = 1; i <= n; i++) fac[i] = fac[i - 1] * i % P;
    	for (int i = 0; i <= n; i++){
    		C[i][0] = C[i][i] = 1;
    		for (int j = 1; j <= (i >> 1); j++)
    			C[i][j] = C[i][i - j] = (C[i - 1][j - 1] + C[i - 1][j]) % P;
    	}
    	sort(a + 1,a + 1 + n);
    	sort(b + 1,b + 1 + n);
    }
    int main(){
    	n = read(); K = read();
    	if ((n - K) & 1) {puts("0"); return 0;}
    	K = ((n - K) >> 1) + K;
    	for (int i = 1; i <= n; i++) a[i] = read();
    	for (int i = 1; i <= n; i++) b[i] = read();
    	init();
    	f[0][0] = 1;
    	for (int i = 1; i <= n; i++){
    		f[i][0] = f[i - 1][0];
    		int pos = n;
    		while (pos && b[pos] >= a[i]) pos--;
    		for (int j = 1; j <= i; j++)
    			f[i][j] = (f[i - 1][j] + f[i - 1][j - 1] * max(pos - j + 1,0) % P) % P;
    	}
    	LL ans = 0,t;
    	for (int i = K; i <= n; i++){
    		t = ((i - K) & 1) ? -1 : 1;
    		ans = ((ans + t * C[i][K] % P * (f[n][i] * fac[n - i] % P) % P) % P + P) % P;
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    
    

    思路2

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #include<map>
    #define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define mp(a,b) make_pair<int,int>(a,b)
    #define cls(s) memset(s,0,sizeof(s))
    #define cp pair<int,int>
    #define LL long long int
    using namespace std;
    const int maxn = 2005,maxm = 100005,INF = 1000000000,P = 1000000009;
    inline int read(){
    	int out = 0,flag = 1; char c = getchar();
    	while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
    	while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
    	return out * flag;
    }
    LL f[maxn][maxn],C[maxn][maxn],fac[maxn],ans[maxn];
    int a[maxn],b[maxn];
    int n,K;
    void init(){
    	fac[0] = 1;
    	for (int i = 1; i <= n; i++) fac[i] = fac[i - 1] * i % P;
    	for (int i = 0; i <= n; i++){
    		C[i][0] = C[i][i] = 1;
    		for (int j = 1; j <= (i >> 1); j++)
    			C[i][j] = C[i][i - j] = (C[i - 1][j - 1] + C[i - 1][j]) % P;
    	}
    	sort(a + 1,a + 1 + n);
    	sort(b + 1,b + 1 + n);
    }
    int main(){
    	n = read(); K = read();
    	if ((n - K) & 1) {puts("0"); return 0;}
    	K = ((n - K) >> 1) + K;
    	for (int i = 1; i <= n; i++) a[i] = read();
    	for (int i = 1; i <= n; i++) b[i] = read();
    	init();
    	f[0][0] = 1;
    	for (int i = 1; i <= n; i++){
    		f[i][0] = f[i - 1][0];
    		int pos = n;
    		while (pos && b[pos] >= a[i]) pos--;
    		for (int j = 1; j <= i; j++)
    			f[i][j] = (f[i - 1][j] + f[i - 1][j - 1] * max(pos - j + 1,0) % P) % P;
    	}
    	ans[n] = f[n][n];
    	for (int i = n - 1; i >= K; i--){
    		ans[i] = f[n][i] * fac[n - i] % P;
    		for (int j = i + 1; j <= n; j++)
    			ans[i] = (ans[i] - C[j][i] * ans[j] % P) % P;
    		ans[i] = (ans[i] + P) % P;
    	}
    	printf("%lld
    ",ans[K]);
    	return 0;
    }
    
    
  • 相关阅读:
    实现类似add(1)(2)(3)的函数
    Chrome安装助手踩坑
    升级webpack4错误处理
    vue项目埋点
    如何理解vue中的v-bind?
    不能不知道的webpack基本配置
    IE9及以下浏览器升级提示
    HTML5常用API
    css中clip属性
    Web开发展望
  • 原文地址:https://www.cnblogs.com/Mychael/p/9028747.html
Copyright © 2011-2022 走看看