容斥原理学习笔记
普通容斥
解说
通俗地来说几个集合并集的大小,就是所有单个集合大小之和减去所有两个集合相交的部分,再加回所有三个集合相交的部分,再减去所有四个集合相交的部分……
\(All \ in \ all\),奇加偶减。
那么我们在实际操作的时候就可以枚举每一个子集(\(DFS\)和二进制都可以),计算其大小,大小为奇数就加上其大小,否则减去其大小。
例题:八
题目
找出\([a,b]\)中能被\(8\)整除却不能被其他一些数整除的数。
输入格式
第一行一个数\(n\),代表不能被整除的数的个数。
第二行\(n\)个数,中间用空格隔开。
第三行两个数\(a,b\),中间一个空格。
输出格式
一个整数,为\([a,b]\)间能被\(8\)整除却不能被那 个数整除的数的个数。
样例输入
3
7764 6082 462
2166 53442
样例输出
6378
解说
够裸的了吧,没啥可说的……
#include<bits/stdc++.h>
using namespace std;
int n,a,b,num[15+3],ans;
typedef long long ll;
ll gcd(ll a,ll b){
if(!b) return a;
return gcd(b,a%b);
}
ll lcm(ll a,ll b){
ll g=gcd(a,b);
return a*b/g;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&num[i]);
scanf("%d%d",&a,&b);
ans=b/8-(a-1)/8;
for(int s=1;s<=(1<<n)-1;s++){
int cnt=0;
ll LCM=8;
for(int i=1;i<=n;i++){
if(s&(1<<i-1)){
cnt++;
LCM=lcm(LCM,num[i]);
}
}
if(cnt%2) ans-=b/LCM-(a-1)/LCM;
else ans+=b/LCM-(a-1)/LCM;
}
printf("%d\n",ans);
return 0;
}
\(Min-Max\)容斥
解说
简而言之就一个式子:
(下面大写字母均表示集合,\(| \ \ |\)表示集合大小)
简单解释一下,假设现在我们非常傻,不会取\(\max\)(\(\min\))这一运算而只会取\(\min\)(\(\max\)),那么我们就可以借助\(Min-Max\)容斥间接求出我们需要的值。
手模一组示例:
假设\(S=\{ 1,2,3\}\),我们想利用每个非空自己的最小值求其最大值。
枚举其非空子集:
\(\{ 1 \}\),贡献为\(1 \times (-1)^2=1\)
\(\{ 2 \}\),贡献为\(2 \times (-1)^2=2\)
\(\{ 3 \}\),贡献为\(3 \times (-1)^2=3\)
\(\{ 1,2 \}\),贡献为\(1 \times (-1)^3=-1\)
\(\{ 1,3 \}\),贡献为\(1 \times (-1)^3=-1\)
\(\{ 2,3 \}\),贡献为\(2 \times (-1)^3=-2\)
\(\{ 1,2,3\}\),贡献为\(1 \times (-1)^4=1\)
加和一下会发现它等于\(3\),正好是最大值。
但是这玩意儿貌似好鸡肋啊……其实它在算期望的时候会非常有用。直接借助例题来说。
例题:礼物加强版
题目
夏川的生日就要到了。作为夏川形式上的男朋友,季堂打算给夏川买一些生日礼物。商店里一共有种礼物。夏川每得到一种礼物,就会获得相应喜悦值\(W_i\)(每种礼物的喜悦值不能重复获得)。每次,店员会按照一定的概率\(P_i\)(或者不拿出礼物),将第\(i\)种礼物拿出来。季堂每次都会将店员拿出来的礼物买下来。没有拿出来视为什么都没有买到,也 算一次购买。
众所周知,白毛切开都是黑的。所以季堂希望最后夏川的喜悦值尽可能地高。
求夏川最后最大的喜悦值是多少,并求出使夏川得到这个喜悦值,季堂的期望购买次数。
输入格式
第一行,一个整数\(N\),表示有\(N\)种礼物。
接下来\(N\)行,每行一个实数\(P_i\)和正整数\(W_i\),表示第\(i\)种礼物被拿出来的概率和可以获得喜悦值。
输出格式
第一行,一个整数表示可以获得的最大喜悦值。
第二行,一个实数表示获得这个喜悦值的期望购买次数,保留\(3\)位小数。
样例
样例输入
3
0.1 2
0.2 5
0.3 7
样例输出
14
12.167
数据范围与提示
对于\(10 \%\)的数据,\(N = 1\)
对于\(30 \%\)的数据,\(N\le5\)
对于\(100 \%\)的数据,\(N \le 25 ,0 < Wi \le 10^9 ,0 < Pi \le 1\text{且}\sum P_i \le 1\)
解说
首先,肯定是所有礼物都全部买下的时候喜悦值最大,所以第一问白送分,同时问题转化为期望买几次可以把所有礼物全部买齐。
本来似乎是有概率\(DP\)做法的,但是加强版\(n \le 25\)的数据范围让我们只能用别的方法。考虑使用\(Min-Max\),我们要解决的最大问题就是现在\(min\)和\(max\)分别代表什么。经过一番思考不难得出,对于一个集合,\(max\)现在代表将集合里的礼物全部买齐的期望,而\(min\)则代表买到集合中礼物任意一件的期望。
显然,此时
所以我们枚举子集挨个计算即可,依然是二进制枚举或者递归均能解决。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int lzw=25+3;
double p[lzw],ans;
ll sum;
int n;
void dfs(int loc,double res,int ch){
if(loc==n+1){
if(!ch) return;
ans+=1.0/res*((ch%2)?1:-1);
return;
}
dfs(loc+1,res,ch);
dfs(loc+1,res+p[loc],ch+1);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
int tmp;
scanf("%lf%d",&p[i],&tmp);
sum+=tmp;
}
printf("%lld\n",sum);
dfs(1,0,0);
printf("%.3lf\n",ans);
return 0;
}
幸甚至哉,歌以咏志。