算法 {容斥原理,二进制子集的容斥}

算法 {容斥原理,二进制子集的容斥}

@MARK_1

容斥原理

定义

給定若干去重集合Si, 則 S 1 ∪ S 2 ∪ . . . ∪ S n = + i S i − + i , j ( S i ∩ S j ) + + i , j , k ( S i ∩ S j ∩ S k ) − . . . S1 \cup S2 \cup ... \cup Sn = +_i Si - +_{i,j}(Si \cap Sj) + +_{i,j,k}(Si\cap Sj \cap Sk) - ... S1S2...Sn=+iSi+i,j(SiSj)++i,j,k(SiSjSk)..., 其中的+ -指的是集合加法,集合減法;
2 n 2^n 2n遍歷全組合, 如果是奇數個項 比如S1S2S3 則執行加法, 否則比如S1S2 則執行減法;
. 比如S1={a,b,e} S2={b,c,e} S3={c,d,e}, 則有S1 U S2 U S3 == (S1+S2+S3) - (S1S2 + S1S3 + S2S3) + (S1S2S3) == {abe,bce,cde} - {be,e,ce} + {e} == {a,b,c,d};

算法

代碼模板

求`ANS = S0 U S1 U S2 U ... U S[n-1]`;

ANS = {};
for( int st = 1; st < (1<<n); ++st){
	vector<int> Ind = `st裡的所有1的下標`;
	S := `S[Ind.front()] 交集 ... 交集 S[Ind.back()]`;
	if( Ind.size() & 1){
		ANS 集合加法 S;
	}
	else{
		ANS 集合減法 S;
	}
}

二进制子集的容斥原理

@LINK: @MARK_0;

性质

容斥的核心是: 去掉重复的元素;

@DELI;

#去除多餘的集合#
T:= S1 U S2 U ... U Sn, 對於Si S R = U j ≠ i S j SR = U_{j \neq i} Sj SR=Uj=iSj, 如果 S i ⊂ S R Si \subset SR SiSRSR == T; 也就是 你可以把Si給去掉;
. 比如S1={a}, S2={b}, S3={c}, S4={a,b}, S5={a,c}, S6={b}, S7={a,b,c}, 那麼S1 U ... U S7 == S1 U S2 U S3, 即可以把S4,S5,S6,S7給去掉, 即原來容斥原理是2^7量級 現在優化為2^3;
例題: @LINK: https://editor.csdn.net/md/?not_checkout=1&articleId=130248080;

@DELI;

来一个经典例题来熟悉容斥原理;

给定 N N N个数 [ a 1 , . . . , a n ] [ a_1, ..., a_n] [a1,...,an], 令布尔函数f(x) = (a1 | x) || (a2 | x) ... || (an | x); (即只要可以被至少一个ai整除, 则x返回True);
. x ∈ [ 1 , M ] x \in [1, M] x[1,M]范围内 f(x) = True的个数;

我们知道 对于一个数 x x x, [1, N]范围内 会有N / x个数可以整除x (即x, 2x, 3x, ...);
. 所以, 可以定义S_x 表示所有可以整除x的元素 所组成的集合, 用C(x)表示 ∣ S x ∣ |S_x| Sx;
. 那么, 答案即为 ∣ S a 1 ∪ S a 2 ∪ . . . ∪ S a n ∣ | S_{a_1} \cup S_{a_2} \cup ... \cup S_{a_n}| Sa1Sa2...San; 根据上面的转换公式, 你需要求 ∣ S i ∩ S j ∣ |S_i \cap S_j| SiSj (这里只是做一般性分析, 当然 ∣ . . . ∣ |...| ∣...∣中 可能包含[1/2/3/…/N]个 S i S_i Si, 这里以2个为例子);
. . 容斥原理, 最最重要的, 就是求这个值, 即若干个Si的并集 的大小; 他要怎么求, 你需要具体问题具体分析, 通常都是用C(x)这个函数 来求他;
. . 在这里, S i ∩ S j S_i \cap S_j SiSj表示: 既可以整除i 又可以整除j的 元素个数; 你必须要搞懂这个逻辑式, 他的本质是 可以整除z = LCM(i,j) (最大公倍数), 因此 ∣ S i ∩ S j ∣ = C ( z ) |S_i \cap S_j| = C(z) SiSj=C(z);
. . 因此, 答案 就是 一堆C(x)的相加减;

容斥原理的前提条件:
容斥原理 一定求的是 N N N个集合的并集的大小, 也就是 遇到容斥原理 你必须要找到一些集合 使得这些结合的并集, 就是答案集合
. 反过来说, 假如答案集合为S, 你必须将他可重复性的划分 (两个子集可以相交) 为若干子集合, 使得这些集合的并集 等于S;

容斥原理的计算方式:
容斥原理的结果, 就是一堆 ∣ S i ∩ S j ∩ S k ∣ |S_i \cap S_j \cap S_k| SiSjSk之和 (他是有符号的, 取决于i,j,k,...的个数, 如果是奇数 则为1, 否则为-1);
. ∣ S i ∩ S j ∩ S k ∣ |S_i \cap S_j \cap S_k| SiSjSk 这个值, 是通过一个函数C(x)来计算的;

容斥原理的常用算法
. 1 1 << N时间

ans = 0;
for( st = 1; st < (1 << N); ++st){
	bit_count = `st中1的个数`;
	z = `st中所有1对应的A[]值的最小公倍数`;
	if( bit_count & 1) ans += C(z); // `C(z) = M / z`;
	else ans -= C(z);
}

. 2 M次循环 @MARK_0
. . 不要认为, 容斥原理就一定是1 << N的时间; 你一定要结合公式, 因为我们知道 容斥原理 即答案 就等于一堆C(x)之和, 在本题中 C(x) = M / z, 自然对于C( > M) 他的值一定是0, 所以可以跳过不管的;
. . 假如题目说了 这N个数 是互不相同的质数, 那么, 对于这 2 n − 1 2^n - 1 2n1个项 ∣ S i ∩ S j ∩ S k ∣ = C ( z ) |S_i \cap S_j \cap S_k| = C( z) SiSjSk=C(z) ( z = L C M ( i , j , k ) z = LCM(i,j,k) z=LCM(i,j,k)) 与 z 形成双射; 即不同的项 对应的z (注意不是C(z) 而是z) 是不同的;
. . 因为这里z的定义是: z = i * j * k, 因为i,j,k都是不同的质数 所以组成z一定不同;
. . 假如把互不相同这个条件去掉, 仅仅只是质数, 那这个性质就不存在了; 比如有两个相同质数p, p, 那么有两个项 都是 ∣ S p ∣ |S_p| Sp 自然他们对应的z是相同的;
. . 假如把质数这个条件去掉, 仅仅只是互不相同, 那这个性质也不存在了; 比如2, 3, 6, 那么 S 6 S_6 S6 S 2 ∩ S 3 S_2 \cap S_3 S2S3 都对应z = 6;
. . 这里有了这个性质, 即这 2 n − 1 2^n - 1 2n1个项 对应的z值均不同; (假如题目中 n = 1000 n = 1000 n=1000 肯定是不能枚举所有的项 来求容斥原理了; 而且, z值可能就无法求出 因为这 n n n个数的乘积 溢出long long); 此时, 一定要结合C(z)这个函数, 因为我们的本质 就是求这个函数; 根据该函数在这道题的定义 C(z) = M / z, 那么显然, 对于C( > M) = 0, 因此 对于所有对应z > M的项 我们根本就无需枚举这些项;
. . 因此, 只考虑z = [1, ..., M]范围内的项, 令MM= min( M, 这n个数的乘积), 那么 对这些项对应的<= MMz值 一定是在[1, ..., MM]范围内的 (但不是满射, 比如z = 4 没有项的z = 4); 因此, 对于一个z值, 设置一个Flag(z)函数 [如果z质因数分解的最高次> 1, 则为0],[如果z的质因数个数为奇数, 则为1],[如果z的质因数个数为偶数, 则为-1]; (这个Flag(z) 就是莫比乌斯函数)
. . 此时原式变为: ∑ i = 1 M C ( i ) ∗ F l a g ( i ) \displaystyle \sum_{i = 1}^{M} C(i) * Flag(i) i=1MC(i)Flag(i), 即 我们将 O ( 2 n ) → O ( M ) O(2^n) \to O(M) O(2n)O(M); 因为本题里 C ( i ) C(i) C(i)是下取整函数, 那么 C ( i ) C(i) C(i)的取值 只有 2 M 2\sqrt{M} 2M 个, 因此 可以再使用下取整的前缀和优化Flag(i)进行前缀和处理, 然后i直接从l跳到r + 1 (即i= [l, l+1, ..., r]对应的C(i)都是相同的); 就从 O ( M ) → O ( M ) O(M) \to O(\sqrt{M}) O(M)O(M );

例题

CSDN--130248080: 基于@MARK_0, 即不是1<<N的枚举, 而是O(M)的枚举, 再用下取整优化到O(sqrt(M))

https://editor.csdn.net/md/?articleId=130220207
. 变形的隔板法; C[i] <= M[i]是合法方案, 他的非法方案为C[i] > M[i], 则使用容斥原理求非法方案, 则合法方案 = 朴素隔板法 - 容斥原理;

二进制子集的容斥原理

@MARK_0;

定义

&N: 二进制长度; &Subs(st): st的子集(比如&Subs(101)={101,100,001,000}); &Raw[st]: 每个二进制对应的价值; 已知&Sum[st]: Raw[st子集]之和; (比如&Sum[101] = Raw[101,100,001,000]之和;
求 某个特定的&Raw[x];

答案: &Raw[st] = (flag(x) * Sum[x])之和 (x为&Subs(st), flag(x) = {x,st}的二进制中1的个数的奇偶性相同? : 1 : -1));
. 比如Raw[101] = Sum[101] - Sum[100] - Sum[001] + Sum[000]; 比如Raw[1101] = Sum[1101] - Sum[1100] - Sum[1001] - Sum[0101] + Sum[1000] + Sum[0100] + Sum[0001] - Sum[0000];

代码

时间是2^N (N为tarST里1的个数);

template< class _Type_> _Type_ ___InExPrinciple_binarySubsets( _Type_ const* _sum, int _tarST){ // `sum`的长度至少是`[tarST+1]`;
    _Type_ ANS = 0;
    auto flag = __builtin_popcount(_tarST)&1;
    for( auto subST = _tarST; ; subST=(subST-1)&_tarST){
        if( (__builtin_popcount(subST)&1) == flag){ ANS += _sum[subST];} else{ ANS -= _sum[subST];}

        if(subST==0){ break;}
    }
    return ANS;
} // ___InExPrinciple_binarySubsets

筆記

假设全集为 S S S, 对于任意 N N N个子集 s 1 , s 2 , . . . , s n s_1, s_2, ..., s_n s1,s2,...,sn, 求这些集合的并集 ∪ \cup 的大小;
. 注意任意这2字, 意味着, s i s_i si可以是空集 可以是全集 可以是任何集合; s i , s j s_i, s_j si,sj 他们可以相交 可以互斥 任何关系; 只要满足 s i ∈ S s_i \in S siS即可;

容斥原理的算法是 O ( 2 n ) O(2^n) O(2n)的, ∣ s 1 ∪ . . . ∪ s n ∣ = ∑ i ∈ [ 1 , n ] ∣ s i ∣ − ∑ i < j ∈ [ 1 , n ] ∣ s i ∩ s j ∣ + ∑ i < j < z ∈ [ 1 , n ] ∣ s i ∩ s j ∩ s z ∣ − . . . \displaystyle |s_1\cup ... \cup s_n| = \sum_{i \in [1,n]} |s_i| - \sum_{i<j \in[1,n]} |s_i \cap s_j| + \sum_{i<j<z \in [1,n]} |s_i \cap s_j \cap s_z| - ... s1...sn=i[1,n]sii<j[1,n]sisj+i<j<z[1,n]sisjsz...;
. 以单个 ∣ s i ∩ . . . ∣ |s_i \cap ...| si...∣为一项, 则一共是有 2 n − 1 2^n - 1 2n1项;

理解容斥原理最本质的角度, 就是文氏图; 一个集合 在文氏图中的面积大小 就对应该集合的元素大小 ∣ s ∣ |s| s;
. 比如, 给定三个集合 a , b , c a,b,c a,b,c, 已知 ∣ a ∣ = 2 , ∣ b ∣ = 3 , ∣ c ∣ = 4 |a| = 2, |b| = 3, |c| = 4 a=2,b=3,c=4, 且 ∣ a ∩ b ∣ = 1 , ∣ a ∩ c ∣ = 1 , ∣ b ∩ c ∣ = 2 |a \cap b| = 1, |a \cap c| = 1, |b \cap c| = 2 ab=1,ac=1,bc=2, 且 ∣ a ∩ b ∩ c ∣ = 1 |a \cap b \cap c| = 1 abc=1;
. . 那么, 我们就可以求出 a ∩ b ∩ c a\cap b \cap c abc这个集合的大小, 即 ∣ a ∩ b ∩ c ∣ = ( 2 + 3 + 4 ) − ( 1 + 1 + 2 ) + ( 1 ) |a \cap b \cap c| = (2 + 3 + 4) - (1 + 1 + 2) + (1) abc=(2+3+4)(1+1+2)+(1);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值