zoukankan      html  css  js  c++  java
  • 容斥原理学习笔记

    普通容斥

    解说

    通俗地来说几个集合并集的大小,就是所有单个集合大小之和减去所有两个集合相交的部分,再加回所有三个集合相交的部分,再减去所有四个集合相交的部分……

    (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
    ",ans);
    	return 0;
    }
    

    (Min-Max)容斥

    解说

    简而言之就一个式子:

    (下面大写字母均表示集合,(| |)表示集合大小)

    [max(S)= sum_{Tsubset S,T eq emptyset} min(T)(-1)^{|T|+1} ]

    [min(S)= sum_{Tsubset S,T eq emptyset} max(T)(-1)^{|T|+1} ]

    简单解释一下,假设现在我们非常傻,不会取(max)(min))这一运算而只会取(min)(max)),那么我们就可以借助(Min-Max)容斥间接求出我们需要的值。

    手模一组示例:

    假设(S={ 1,2,3}),我们想利用每个非空自己的最小值求其最大值。

    枚举其非空子集:

    ({ 1 }),贡献为(1 imes (-1)^2=1)

    ({ 2 }),贡献为(2 imes (-1)^2=2)

    ({ 3 }),贡献为(3 imes (-1)^2=3)

    ({ 1,2 }),贡献为(1 imes (-1)^3=-1)

    ({ 1,3 }),贡献为(1 imes (-1)^3=-1)

    ({ 2,3 }),贡献为(2 imes (-1)^3=-2)

    ({ 1,2,3}),贡献为(1 imes (-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 \%)的数据,(Nle5)

    对于(100 \%)的数据,(N le 25 ,0 < Wi le 10^9 ,0 < Pi le 1 ext{且}sum P_i le 1)

    解说

    首先,肯定是所有礼物都全部买下的时候喜悦值最大,所以第一问白送分,同时问题转化为期望买几次可以把所有礼物全部买齐。

    本来似乎是有概率(DP)做法的,但是加强版(n le 25)的数据范围让我们只能用别的方法。考虑使用(Min-Max),我们要解决的最大问题就是现在(min)(max)分别代表什么。经过一番思考不难得出,对于一个集合,(max)现在代表将集合里的礼物全部买齐的期望,而(min)则代表买到集合中礼物任意一件的期望

    显然,此时

    [min(T)= frac{1}{sum_{iin T} p_i} ]

    所以我们枚举子集挨个计算即可,依然是二进制枚举或者递归均能解决。

    代码

    #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
    ",sum);
    	dfs(1,0,0);
    	printf("%.3lf
    ",ans);
    	return 0;
    }
    

    幸甚至哉,歌以咏志。

  • 相关阅读:
    AmazonS3 替换HDFS 方案
    SQL Server 内存管理
    SQL Server I/O 问题的诊断分析
    共享内存 设计原理-shm
    机器学习经典算法笔记-Support Vector Machine SVM
    ACGAN 论文笔记
    CGAN 论文笔记
    《Image Generation with PixelCNN Decoders》论文笔记
    《Iterative-GAN》的算法伪代码整理
    《Deep Learning Face Attributes in the Wild》论文笔记
  • 原文地址:https://www.cnblogs.com/DarthVictor/p/13741027.html
Copyright © 2011-2022 走看看