[bzoj3622]已经没有什么好害怕的了——容斥or二项式反演+DP

题目大意:

给定两个长度为\(n\)的序列,求有多少种匹配方式,使得\(a_i<b_i\)的个数恰好为\(k\)个。

思路:

据说是一道二项式反演的经典例题了。
首先如果要求正好等于\(k\)个的是不太好求的,我们可以考虑求出至少为\(k\)个的方案数。
首先先把两个序列都按照从小到大的顺序排好序,然后以序列\(b\)为对象dp。
我们设\(f_{i,j}\)表示前\(i\)个数里面强制确定了\(j\)个\(a_i<b_i\)关系的方案数,记\(c_i\)表示在\(a\)中有多少个数<\(b_i\),于是可以得到方程
\[
f_{i,j}=f_{i-1,j}+f_{i-1,j-1}\times (c_{i}-j+1)\\]
由于排了序的缘故,第\(i\)个数做出的决策一定有\(c_i-j+1\)种。
当然,最后\(f_{n,i}\)还需要乘以\((n-i)!\)。
这时我们可以得到最后的答案\(\{g\}\)与\(f_{n,i}\)的关系为:
\[
f_{i}=\sum_{j=i}^{n}{j \choose i}\times g_{j}\\]
根据二项式反演可得:
\[
g_{i}=\sum_{j=i}^{n}(-1)^{j-i}\times {j\choose i}\times f_j
\]
直接\(O(n)\)反演即可。
当然观察到\(f_i\)和\(g_i\)的关系之后,我们可以倒推利用容斥来推出\(g_i\),此时可得:
\[
g_i=f_i-\sum_{j=i+1}^{n}{j\choose i}g_j
\]
这也不失为一种很好的理解方法。

#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define debug(x) cout<<#x<<"="<<x<<" "
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;

using namespace std;

void File(){
    freopen("bzoj3622.in","r",stdin);
    freopen("bzoj3622.out","w",stdout);
}

template<typename T>void read(T &_){
    _=0; T f=1; char c=getchar();
    for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
    for(;isdigit(c);c=getchar())_=(_<<1)+(_<<3)+(c^'0');
    _*=f;
}

const ll mod=1e9+9;
const int maxn=2000+10;
int n,m,a[maxn],b[maxn];
ll fac[maxn],ifac[maxn],c[maxn],f[maxn][maxn],g[maxn],ans;

ll qpow(ll x,ll y){
    ll ret=1; x%=mod;
    while(y){
        if(y&1)ret=ret*x%mod;
        x=x*x%mod;
        y>>=1;
    }
    return ret;
}

void ad(ll &_,ll __){_=(_+__)%mod;}

ll C(int x,int y){
    return fac[x]*ifac[y]%mod*ifac[x-y]%mod;
}

int main(){
    File();
    read(n),read(m);
    REP(i,1,n)read(a[i]);
    REP(i,1,n)read(b[i]);

    if((n-m)%2)return puts("0"),0;
    m=(n-m)/2;

    fac[0]=1;
    REP(i,1,n)fac[i]=fac[i-1]*i%mod;
    ifac[n]=qpow(fac[n],mod-2);
    DREP(i,n-1,0)ifac[i]=ifac[i+1]*(i+1)%mod;

    sort(a+1,a+n+1);
    sort(b+1,b+n+1);

    int p=0;
    REP(i,1,n){
        while(p<n && a[p+1]<b[i])++p;
        c[i]=p;
    }

    f[0][0]=1;
    REP(i,1,n)REP(j,0,i){
        ad(f[i][j],f[i-1][j]);
        if(j)ad(f[i][j],f[i-1][j-1]*(c[i]-j+1)%mod);
    }

    REP(i,0,n)f[n][i]=f[n][i]*fac[n-i]%mod;

    /*REP(i,m,n)ad(ans,((i-m)%2 ? -1 : 1)*C(i,m)*f[n][i]%mod);
    printf("%lld\n",(ans+mod)%mod);*/

    DREP(i,n,m){
        g[i]=f[n][i];
        REP(j,i+1,n)ad(g[i],-C(j,i)*g[j]%mod);
    }

    printf("%lld\n",(g[m]+mod)%mod);

    return 0;
}

原文地址:https://www.cnblogs.com/ylsoi/p/10223764.html

时间: 2024-09-29 16:23:08

[bzoj3622]已经没有什么好害怕的了——容斥or二项式反演+DP的相关文章

[bzoj3622]已经没有什么好害怕的了_动态规划_容斥原理

bzoj-3622 已经没有什么好害怕的了 题目大意: 数据范围:$1\le n \le 2000$ , $0\le k\le n$. 想法: 首先,不难求出药片比糖果小的组数. 紧接着,我开始的想法是 $f_{(i,j)}$表示前$i$个糖果中,满足糖果比药片大的组数是$j$的方案数. 进而发现需要将两个数组排序. 到这里一切都很正常,但是我们发现了一个问题:就是我在转移的时候,分两种情况讨论.第一种是当前糖果配对的药片比自己大,第二种是比自己小. 这样的话我需要乘上两个组合数. 但是我们仔细

Bzoj3622 已经没有什么好害怕的了

这题的题面全是图,而博客园的图随时有挂的危险…… 反正能去BZOJ找原题,已经没什么好害怕的了 3622: 已经没有什么好害怕的了 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 476  Solved: 231 Description Input Output Sample Input 4 2 5 35 15 45 40 20 10 30 Sample Output 4 HINT 输入的2*n个数字保证全不相同. 还有输入应该是第二行是糖果,第三

BZOJ3622已经没有什么好害怕的了

Description Input Output Sample Input 4 2 5 35 15 45 40 20 10 30 Sample Output 4 HINT 输入的2*n个数字保证全不相同. 还有输入应该是第二行是糖果,第三行是药片 题解: 记得以前在学校题库中做过这道题. 先判断k是否可能实现,若能,求出需要有恰好多少对糖果比巧克力大. 现将两组数各自从小到大排序.用dp[i,j]表示给前i个糖果中的j个安排数值更低的的巧克力的方案数. 设第i个糖果比前k个巧克力数值大,则除了d

dp 容斥 bzoj3622 已经没有什么好害怕的了

https://www.lydsy.com/JudgeOnline/problem.php?id=3622 XJOI题 不过没做过.. 容斥计数啊 先排序 记\(f[i][j]\)为前\(i\)位有\(j\)位固定且\(a[j] > b[k]\)的方案数 \[f[i][j] = f[i - 1][j] + f[i - 1][j - 1] \times (last - (j - 1))\] \(last\)为最大的\(k\)使得\(a[i] > b[k]\) 有 \[f[n][i] = \sum

P4859 已经没有什么好害怕的了(二项式反演)

题目 思路 显然是恰好有\(\frac{n+k}{2}\)组\(a>b\) 令\(f(i,j)\)表示前\(i\)个糖果,已经有\(j\)组\(a>b\),剩下的没管的方案数 对\(a\)数组从小到大排序,设\(r_i\)表示比\(a_i\)小的\(b\)个数,那么\(r_i\)是递增的 有状态转移方程\(f(i,j) = f(i-1,j) + f(i-1,j-1) \times (r_i-j+1)\) 对于每个\(f(n,i)\),由于剩下的\(n-i\)对不知道大小情况,那么有\(f(n,

BZOJ 3622(已经没有什么好害怕的了-Dp+容斥原理)

3622: 已经没有什么好害怕的了 Time Limit: 10 Sec  Memory Limit: 256 MB Submit: 7  Solved: 6 [Submit][Status] Description Input Output Sample Input 4 2 5 35 15 45 40 20 10 30 Sample Output 4 HINT Source 2014湖北省队互测week2 PS:本题的数据中能量互不相同. 1.我们计算出糖果>药片的组数=k 2.我们计算出f[

[BZOJ 3622]已经没有什么好害怕的了(Dp+容斥原理)

Description 图片略 Solution 对啊,已经没有什么好害怕的了 没有头的麻美学姐还是很萌的(雾 排序预处理p[i]为b中小于a[i]的最大的数的标号 f[i][j]表示前i个糖果使得糖果大于药片的至少有j组 则f[i][j]=f[i-1][j]+f[i-1][j-1]*(p[i]-j+1) 容斥得g[j]=f[n][j]*(n-j)!-∑g[k]*C(j,k) (j+1<=k<=n) #include<iostream> #include<cstdio>

[BZOJ 3622]已经没有什么好害怕的了

世萌萌王都拿到了,已经没有什么好害怕的了——    (作死) 笑看哪里都有学姐,真是不知说什么好喵~ 话说此题是不是输 0 能骗不少分啊,不然若学姐赢了,那么有头的学姐还能叫学姐吗?  (作大死) 这题的数据就告诉我们这是赤裸裸的 dp ,不过要加个容斥而已 注意到我们可以算出一共需要 s 组满足糖果数 > 药片数 (在这里显然有个特判,即 n-k 为奇数时,答案一定为 0 ) 我们将两个读入的数组排序 令 next[i] 表示最大的 j 满足 糖果[i]>药片[j] 令 f[i][j] 表示

二项式反演及其应用

概念 二项式反演为一种反演形式,常用于通过 "指定某若干个" 求 "恰好若干个" 的问题. 注意:二项式反演虽然形式上和多步容斥极为相似,但它们并不等价,只是习惯上都称之为多步容斥. 引入 既然形式和多步容斥相似,我们就从多步容斥讲起. 我们都知道:$|A\cup B|=|A|+|B|-|A\cap B|$ ,这其实就是容斥原理. 它的一般形式为: $$|A_1\cup A_2\cup...\cup A_n|=\sum\limits_{1\le i\le n}|A_