Luogu4859 二项式反演

今天学了一个叫二项式反演的有趣东西.

其实它的核心式子就两个

若\(g_i=\sum_{j=i}^n\binom{j}{i}f[j]\)

那么\(f_i=\sum_{j=i}^n(-1)^{j-i}\binom{j}{i}g[j]\)

证明是用容斥证的.



现在我们看这道题.

题目链接

我们知道答案就是\(a>b\)的对数为\(\frac{n+k}{2}\)的方案数.

令\(x=\frac{n+k}{2}\)

考虑普通\(dp\).

用\(f[i][j]\)表示前\(i\)个数,已经有\(j\)对\(a>b\)的方案.

那么\(f[i][j]=f[i-1][j]+(lb[i]-j)*f[i-1][j-1]\)

其中\(lb[i]\)=\(lowerbound(b,a[i])\)

我们令\(g[i]\)表示\(a>b\)的对数\(\geq x\)的方案数.

那么\(g[i]=f[n][i]*(n-i)!\)

就是钦点了\(i\)对\(a>b\)匹配,然后剩下的随意匹配的方案数.

用\(a[i]\)表示刚好有\(i\)对\(a>b\)的方案数.

考虑对于\(\forall j\in [i,n],a[j]\)在\(g[i]\)中计算了\(C_j^i\)次.

这个证明..显然

那么我们列出方程\(g_i=\sum_{j=i}^n\binom{j}{i}a_j\)

二项式反演得\(a_i=\sum_{j=i}^n(-1)^{j-i}\binom{j}{i}g_j\)

于是只要先\(O(n^2)dp\)一下,然后再\(O(n)\)计算一下即可.

代码如下

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#define N (2010)
#define P (1000000009)
#define inf (0x7f7f7f7f)
#define rg register int
#define Label puts("NAIVE")
#define spa print(‘ ‘)
#define ent print(‘\n‘)
#define rand() (((rand())<<(15))^(rand()))
typedef long double ld;
typedef long long LL;
typedef unsigned long long ull;
using namespace std;
inline char read(){
    static const int IN_LEN=1000000;
    static char buf[IN_LEN],*s,*t;
    return (s==t?t=(s=buf)+fread(buf,1,IN_LEN,stdin),(s==t?-1:*s++):*s++);
}
template<class T>
inline void read(T &x){
    static bool iosig;
    static char c;
    for(iosig=false,c=read();!isdigit(c);c=read()){
        if(c==‘-‘)iosig=true;
        if(c==-1)return;
    }
    for(x=0;isdigit(c);c=read())x=((x+(x<<2))<<1)+(c^‘0‘);
    if(iosig)x=-x;
}
inline char readchar(){
    static char c;
    for(c=read();!isalpha(c);c=read())
    if(c==-1)return 0;
    return c;
}
const int OUT_LEN = 10000000;
char obuf[OUT_LEN],*ooh=obuf;
inline void print(char c) {
    if(ooh==obuf+OUT_LEN)fwrite(obuf,1,OUT_LEN,stdout),ooh=obuf;
    *ooh++=c;
}
template<class T>
inline void print(T x){
    static int buf[30],cnt;
    if(x==0)print(‘0‘);
    else{
        if(x<0)print(‘-‘),x=-x;
        for(cnt=0;x;x/=10)buf[++cnt]=x%10+48;
        while(cnt)print((char)buf[cnt--]);
    }
}
inline void flush(){fwrite(obuf,1,ooh-obuf,stdout);}
int n,m,a[N],b[N],lb[N],k;
LL f[N][N],g[N],ans,jc[N],inv[N];
LL ksm(LL a,int p){
    LL res=1;
    while(p){
        if(p&1)res=(res*a)%P;
        a=(a*a)%P,p>>=1;
    }
    return res;
}
LL C(int n,int m){return jc[n]*inv[n-m]%P*inv[m]%P;}
int main(){
    read(n),read(m),f[0][0]=1,jc[0]=inv[0]=1;
    if((n+m)&1){puts("0"),exit(0);}else k=(n+m)/2;
    for(int i=1;i<=n;i++)jc[i]=(jc[i-1]*1ll*i)%P,inv[i]=ksm(jc[i],P-2);
    for(int i=1;i<=n;i++)read(a[i]);
    for(int i=1;i<=n;i++)read(b[i]);
    sort(a+1,a+n+1),sort(b+1,b+n+1);
    for(int i=1;i<=n;i++)lb[i]=lower_bound(b+1,b+n+1,a[i])-b;
    for(int i=1;i<=n;i++){
        f[i][0]=f[i-1][0];
        for(int j=1;j<=i;j++)
        f[i][j]=(f[i-1][j-1]*(LL)max(0,lb[i]-j)%P+f[i-1][j])%P;
    }
    for(int i=1;i<=n;i++)g[i]=f[n][i]*jc[n-i]%P;
    for(int i=k;i<=n;i++){
        LL tp=((i-k)&1)?-1:1;
        (ans+=(1ll*tp*C(i,k)%P*g[i])%P)%=P;
    }
    printf("%lld\n",(ans+P)%P);
}

原文地址:https://www.cnblogs.com/Romeolong/p/10119628.html

时间: 2024-10-30 00:32:49

Luogu4859 二项式反演的相关文章

二项式反演

问:给你k种颜色,你必须用上所有颜色去涂满n个相邻的格子,并且要求相邻格子的颜色不同,求方案数. 我们设必须用 i 种颜色两两不相邻的涂格子的方案数为 b(i) ; 很明显: ,我们令 a(k)=k·(k-1)n-1 , 然后有. 如果你知道二项式反演的话,那么这个问题就已经解决了,因为. 是不是觉得二项式反演很厉害,下面我将给出它的证明. 二项式反演公式: 证明: 然后让我们对进行分析: 我们预热一下: 有A,B,C,D,E,F,G 7个人,我们要先从中选出4个候选人,再从中选出3个作为mas

2014ACM/ICPC亚洲区西安站现场赛 F color(二项式反演)

题意:小球排成一排,从m种颜色中选取k种颜色给n个球上色,要求相邻的球的颜色不同,求可行的方案数,答案模1e9+7.T组数据,1<= n, m <= 1e9, 1 <= k <= 1e6, k <= n, m 分析: a(k)表示用不超过k种颜色染n个位置,两两相邻颜色不相同的总数,很简单a(k)=k(n-1)^(k-1) b(k)表示恰好用k种颜色 很显然a(k)=ΣC(k,i)b(i),我们知道a,想知道b,这里就用到二项式反演 那么b(k)=ΣC(k,i)*i*(-1)

二项式反演学习笔记

这是一篇防遗忘的二项式反演证明博客 在此不给出精妙的容斥证明,开始推代数证明 众所周知二项式反演有两个形式 \(f(n) = \sum_{i = 0}^{n} (-1)^{i}\binom{n}{i}g(i) \Leftrightarrow g(n) = \sum_{i = 0}^{n} (-1)^{i} \binom{n}{i}f(i)\) 这个式子简直妙啊--太对称了 然而它更常用的形式是这个 \(f(n) = \sum_{i = 0}^{n}\binom{n}{i}g(i) \Leftri

[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\)中有多少个数<\

CF gym 101933 K King&#39;s Colors —— 二项式反演

题目:http://codeforces.com/gym/101933/problem/K 其实每个点的颜色只要和父亲不一样即可: 所以至多 i 种颜色就是 \( i * (i-1)^{n-1} \),设为 \( f(i) \),设恰好 i 种颜色为 \( g(i) \) 那么 \( f(i) = \sum\limits_{j=0}^{i} C_{i}^{j} * g(j) \) 二项式反演得到 \( g(i) = \sum\limits_{j=0}^{k} (-1)^{k-j} * C_{k}

UVAlive-7040 color(组合数学,二项式反演)

链接:vjudge 题目大意:有一排方格共 $n$ 个,现在有 $m$ 种颜色,要给这些方格染色,要求相邻两个格子的颜色不能相同.现在问恰好用了 $k$ 种颜色的合法方案数.答案对 $10^9+7$ 取模.$T$ 组数据. $1\le T\le 300,1\le n,m\le 10^9,1\le k\le 10^6,k\le \min(n,m)$.大多数数据中 $k$ 很小.(smg啊……) 经典的二项式反演例题. 我们令 $f(x)$ 为一共有 $x$ 种颜色,恰好用了 $x$ 种颜色的方案数

二项式反演及其应用

概念 二项式反演为一种反演形式,常用于通过 "指定某若干个" 求 "恰好若干个" 的问题. 注意:二项式反演虽然形式上和多步容斥极为相似,但它们并不等价,只是习惯上都称之为多步容斥. 引入 既然形式和多步容斥相似,我们就从多步容斥讲起. 我们都知道:$|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_

二项式反演与错排问题

二项式反演与错排问题 常见简单组合恒等式: \(C_n^m=C_n^{n-m}\) \(C_n^m=C_n^{m-1}+C_{n-1}^{m-1}\) \(\sum_{i=0}^{n}C_n^i=2^i\) \(\sum_{i=0}^{n}(-1)^i*C_n^i=[n=0]\) 3.4.证明:由二项式定理易证. 令\(x=1,y=1\),可得3式 令\(x=1,y=-1\), 可得4式 二项式反演: 假设存在两个函数f,g.满足: \[ f_n=\sum_{i=0}^{n}C_n^i*g_i

[bzoj4665]小w的喜糖_二项式反演

小w的喜糖 题目链接:https://lydsy.com/JudgeOnline/problem.php?id=4665 数据范围:略. 题解: 二项式反演裸题. $f_{i,j}$表示,前$i$种钦定$j$拿到自己种类糖果的方案数. 求完了之后可以二项式反演回来即可. 代码: #include <bits/stdc++.h> using namespace std; typedef long long ll; const int mod = 1000000009 ; int n, m; ll