容斥
二项式定理
证明:
有 \(n\) 项 \((a + b)\), 考虑对于最终 \(a ^ k \cdot b ^ {n - k}\) 的贡献:
每一项选 \(a\) 或 \(b\) 然后相乘累加, 那么总共从 \(n\) 项里选 \(k\) 个 \(a\), 其余都选 \(b\)
常见带入:
二项式反演
下面这个式子的 \(g(i)\) 当做上面的 \(g(i) \cdot (-1) ^ i\), 然后带入易得
证明第一个:
充要条件, 那么左边可推出右边, 右边可推出左边
由于这两个式子对称, 这里就偷懒只证明一个方向好了
例: 充分性: 左 \(\Rightarrow\) 右
写在一个矩阵里比较直观(对于推导不是必要的), 观察右下角即为 \(g_n\) 与等式左边相同, 若要成立说明前面一片是 \(0\):
改成竖向求和
解释一下组合数: \(i \in j \in n\), 画一个集合图可得
然后最后面一段像是二项式定理, 把 \(j\) 换元成原来的 \(j-i\) 比较直观 (换元可以参考上面的矩阵的规律)
min-max 容斥
\(\max(S), \min(S)\) 分别表示 \(S\) 中的最大,最小元素
那么有这样两个式子:
证明:
第一个:
将元素从大到小排序 \(\{a_1, a_2, \dots, a_n\}\), 当 \(a_{x + 1}\) 作为 \(\min(T)\) 时,
他的系数是 \(\sum_{i=0}^x \binom{x}{i} (-1) ^ {i + 1 - 1}\) , 这是 \((-1 + 1) ^ x\) 的二项式展开,
那么当 \(x = 0\) 时系数为 \(1\) 否则为 \(0\), 即 \(\max(S) = \min(\{ a_1 \})\)
第二个:
从小到大排序, 其余一样
kth min-max 容斥
设 \(\max_{k}(S), \min_{k}(S)\) 分别为 \(S\) 中第 \(k\) 大/小 的元素
设 \(\max_k(S) = \sum_{T \subseteq S} F(|T|) \min(T)\), 求得这个系数 \(F(|T|)\)
模仿上面的套路, 尝试用 \(\min(T)\) 来表示 \(\max_{k}(S)\) (\(\min_{k}(S)\) 同理)
将元素从大到小排序 \(\{a_1, a_2, \dots, a_n\}\), 当 \(a_{x + 1}\) 作为 \(\min(T)\) 时,
使得当 \(x = k - 1\) 时系数为 \(1\) 否则为 \(0\)
根据上面的关系可得 \(\sum_{i=0}^x \binom{x}{i} \cdot F(i + 1) = [x = (k - 1)]\)
套用二项式反演 \(f(i) = F(i + 1), g(i) = [i = (k - 1)]\)
则
所以
同理
例题
[HDU4336]Card Collector <min-max 容斥>
题意:
给出每种卡牌出现的概率(和为 1 , \(n \le 20\)), 每买一次按上述概率抽卡, 求买到所有卡的期望次数
Sol:
设 \(E(\max(S))\) 为买到 \(S\) 中剩下的最后一张卡的期望次数, 即答案(理解)
设 \(E(\min(S))\) 为买到 \(S\) 中第一张卡的期望次数, 显然是 \(\frac{1}{\sum_{i \in T} p_i}\)
预处理 + 枚举子集
typedef double LDB;
int n;
const int MAXN = (1 << 20) + 10;
int cnt[MAXN], d[MAXN];
LDB p[30], mnp[MAXN];
int lowbit(int x) { return x & (-x); }
int main()
{
while (scanf("%d", &n) != EOF)
{
d[1] = 0;
for (int i = 1; i <= n; ++ i)
{
scanf("%lf", &p[i]);
d[1 << i] = i;
}
int S = (1 << n) - 1;
mnp[0] = cnt[0] = 0;
for (int i = 1; i <= S; ++ i)
{
mnp[i] = mnp[i & (i - 1)] + p[d[lowbit(i)] + 1];
cnt[i] = cnt[i & (i - 1)] + 1;
}
LDB ans = 0;
for (int T = S; T != 0; T = (T - 1) & S)
ans += (cnt[T] % 2 == 1 ? 1 : -1) * 1 / mnp[T];
printf("%lf\n", ans);
}
return 0;
}
BZOJ4036: [HAOI2015]按位或
刚开始你有一个数字0,每一秒钟你会随机选择一个[0,2^n-1]的数字,与你手上的数字进行或(c++,c的|,pascal
的or)操作。选择数字i的概率是p[i]。保证0<=p[i]<=1,Σp[i]=1问期望多少秒后,你手上的数字变成2^n-1。
Sol:
先套用 min-max, \(E(\max(S))\) 表示到达最后一个元素的期望, \(E(\min(S))\) 到达第一个元素的期望
考虑 \(E(\min(S))\) 怎么求, 当没有到达第一个元素的时候, 当前到达的集合和 \(S\) 是没有交集的, 这时需要或上一个与 \(S\) 有交集的数即可
于是发现既然没有到达之前都是无交集, 而且此时选择的数的概率仍然是这样, 那么无论何时概率都是 \(\sum_{t \cap S \ne \emptyset} p[t]\),
也就是说和上一题一样
在考虑如何快速求这个
尝试转化成 FWT 能用的形式, 设 sum[S] = \(\sum_{t \cap S \ne \emptyset} p[t]\)
则
后面那段就是 FWT and 的通式, 一遍 FWT 求得
void FWT(double * a, int len)
{
// FWT(A) = merge(FWT(A0) + FWT(A1), FWT(A1)) ;
for (int mid = 1; mid < len; mid <<= 1)
for (int i = 0; i < len; i += (mid << 1))
for (int j = 0; j < mid; ++ j)
a[i + j] += a[i + j + mid];
}
int main()
{
n = in();
int S = (1 << n) - 1;
for (int i = 0; i <= S; ++ i) scanf("%lf", &p[i]), totsum += p[i];
for (int i = 1; i <= S; ++ i)
{
int sub = i & (i - 1);
cnt[i] = cnt[sub] + 1;
}
for (int i = 0; i <= S; ++ i)
if (i < S - i) swap(p[i], p[S - i]);
FWT(p, S + 1);
double ans = 0;
for (int T = S; T != 0; T = (T - 1) & S)
{
if (fabs(totsum - p[T]) <= 0) return printf("INF\n"), 0;
ans += (cnt[T] % 2 == 1 ? 1 : -1) * 1.0 / (totsum - p[T]);
}
printf("%.6lf\n", ans);
return 0;
}