容斥原理的思想大家都应该挺熟悉的,然后补集转化其实就是容斥原理的一种应用。

一篇讲容斥的博文https://www.cnblogs.com/gzy-cjoier/p/9686787.html

当我们遇到正面解决很困难的问题,我们可以考虑从它的反面去思考,如果反面容易计算的话那么我们就可以用补集转化的思想先计算反面再计算正面(尤其是计数类问题)。

Min-Max容斥是一个十分有用的定理,尤其是在计算概率期望上有

一般来说:这里的Emax(S)是代表出现S所有元素的期望,Emin(T)是出现T任何一个元素的期望。

这里直接总结几道题目:

 洛谷P3349 小星星

题意:给出n个点m条边的图,再给n个点的树。问有多少种方案把树映射到图上,且树上对应边图上也有。

解法:这道题解法十分巧妙地运用了容斥原理。

一个比较容易[想到的暴力解法是设计dp[i][j][S]代表把树上i点映射到图上j点后的图上点使用情况是S。那么转移也比较简单dp[x][i][S]+=dp[y][j][S'] (y是x树上的儿子,ij两点在图上有边,S-S’=i)。但是此题的n达到17,这个解法严重超时。

我们想办法在上一个办法优化,思考这个暴力办法超时的主要原因是加入了S这个状态,假如我们去掉S这个纬度,那么点的使用情况就无法得到掌握就会出现图上某个点被重复映射的不合法情况,同时去掉的好处是dp方程时间很好写且时间复杂度大大降低:dp[x][i]+=dp[y][j] (y是x树儿子,ij两点在图上有边)。于是我们想办法去掉重复映射这种情况,怎么做呢?洛谷题解上大佬提供方法十分巧妙:既然出现重复映射就是图上有某些点没用到,那么我们运用容斥原理,恰好用了n个点方案=不加限制的全集-不加限制的用n-1点方案+不加限制用n-2个点方案........这样一直下去。

#include<bits/stdc++.h>
using namespace std;
const int N=;
typedef long long LL;
int n,m,mp[N][N];
vector<int> G[N]; LL dp[N][N]; //dp[i][j]表示点i映射到j的方案数(可重复映射)
bool vis[N],ban[N]; //是否已经映射/点是否是禁止映射的
void dfs(int x) {
vis[x]=;
for (int i=;i<=n;i++) dp[x][i]=;
for (int i=;i<G[x].size();i++) {
int y=G[x][i];
if (vis[y]) continue;
dfs(y);
for (int j=;j<=n;j++) {
LL tmp=;
for (int k=;k<=n;k++) tmp+=dp[y][k]*(!ban[j] && !ban[k] &&mp[j][k]);
dp[x][j]*=tmp;
}
}
} int main()
{
cin>>n>>m;
for (int i=;i<=m;i++) {
int x,y; scanf("%d%d",&x,&y);
mp[x][y]=mp[y][x]=; //原图
}
for (int i=;i<n;i++) {
int x,y; scanf("%d%d",&x,&y);
G[x].push_back(y); //树
G[y].push_back(x);
}
int ALL=(<<n)-;
long long ans=;
for (int i=;i<=ALL;i++) {
int cnt=;
for (int j=;j<=n;j++) vis[j]=ban[j]=;
for (int j=;j<=n;j++)
if ((<<j-)&i) cnt++,ban[j]=;
dfs();
long long tmp=;
for (int j=;j<=n;j++) tmp+=dp[][j];
ans+=(cnt% ? - : )*tmp;
}
cout<<ans<<endl;
return ;
}

洛谷P3175 按位或

题意:刚开始你有一个数字0,每一秒钟你会随机选择一个[0,2^n-1]的数字与之做或操作。选择数字i的概率是p[i]。保证0<=p[i]<=1,Σp[i]=1问期望多少秒后,你手上的数字变成2^n-1。

解法:这道题学了Min-Max容斥和FWT之后会变得一些简单。解法是参考https://blog.csdn.net/qq_30974369/article/details/81911124 yyb巨佬的。

这题看到比较容易想到Min-Max容斥,是计算Emax(2^n-1)这个东西并不好算,我们考虑用Min-Max容斥转化为计算Emin(T€2^n-1)。

怎么计算Emin呢?然后根据离散随机变量的期望公式 E(x)=1/p 得到      

那么问题变成怎么计算sigma(p[G]) (GΛT≠ø),即所有和T有交集的几个G的概率和,其实这个东西也很难算,我们只好再次利用补集转化思想:所有有交集的G=1-所有没有交集的L(且没有交集的L=T的补集的所有子集)。

于是我们就想办法预处理出元素数据所有的子集的概率和,这个可以用FWT计算得到。

#include<bits/stdc++.h>
using namespace std;
const int M=<<;
const double eps=1e-;
int n,cnt[M],N;
double P[M],ans; void prework() {
for(int i=;i<N;i<<=)
for(int p=i<<,j=;j<N;j+=p)
for(int k=;k<i;++k)
P[i+j+k]+=P[j+k];
} int main()
{
scanf("%d",&n);N=<<n;
for(int i=;i<N;++i)scanf("%lf",&P[i]),cnt[i]=cnt[i>>]+(i&);
prework();
for(int i=;i<N;++i)
if(-P[(N-)^i]>1e-)
ans+=((cnt[i]&)?:-)/(-P[(N-)^i]);
if(ans<eps)puts("INF");else printf("%.10lf\n",ans);
return ;
}

BZOJ 2169 连边

题意:有N个点(编号1到N)组成的无向图,已经为你连了M条边。请你再连K条边,使得所有的点的度数都是偶数。求有多少种连的方法。要求你连的K条边中不能有重边,但和已经连好的边可以重。

解法:日常不会做,解法参考https://www.cnblogs.com/NaVi-Awson/p/7581516.html这位大佬的,写得非常好。

dp[i][j]代表添加i条边之后剩下j奇点的方案数。那么写状态转移方程

dp[i][j]+=dp[i-1][j+2] * (C(j+2,2))  代表从j+2个奇点中选2个连一条边

dp[i][j]+=dp[i-1][j-2] * (C(n-j+2,2))  代表从n-j+2个非奇点中选两个连一条边

dp[i][j]+=dp[i-1][j] * ((j)*(n-j))  代表从j个奇点和n-j个偶点中各选一个连边

但是到目前为止我们还没考虑去掉题目中禁止的重边问题。这里要用到容斥原理减去。

先说结果:dp[i][j]-=dp[i-2][j] * (C(n,2)-(i-2)) ,解释一下:因为n个点完全图上有n*(n-1)/2条边,dp[i-2][j]代表的是已经选的前i-2条边是没有重复的方案。那么到了i条边我们就得考虑减去第i条边与之前某一条边重复了那么第i条边有多少种选择(C(n,2)-(i-2)),那么重复的方案数就是 dp[i-2][j]*(C(n,2)-(i-2)) 。

对于这个重复数可能还是会有些疑惑:例如为什么可选边是C(n,2)-(i-2),因为其实通过开始的三条方程计算得到的结果会包含n点完全图的任一条边且此时选的第i条边会与每一条边重合,换句话来说就是只计算完前3条方程时候第i条边的方案数是不加任何限制能任意重合的,那么我们也就要减去第i条边的任意必重合方案。

综合上面4条就得到状态转移方程了,但是要注意上面的方程是带有顺序的,但是题目要求方案数是没有顺序的,所有dp[i][j]/=i。

HDU-5072 补集转化+容斥原理

题意:抽象起来核心问题就是在完全图上求同色/异色三角形数量。

解法:这道题很有意思值得一做,解法我曾经写过https://www.cnblogs.com/clno1/p/11490374.html

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+;
int n,m,a[N],mul[N],e[N];
vector<int> fac[N]; void prework() { //预处理1-100000的因子
for (int i=;i<=;i++) {
int n=i;
for (int j=;j*j<=n;j++) {
if (n%j==) {
fac[i].push_back(j);
while (n%j==) n/=j;
}
}
if (n>) fac[i].push_back(n);
}
} int main()
{
prework();
int T; cin>>T;
while (T--) {
scanf("%d",&n);
for (int i=;i<=n;i++) scanf("%d",&a[i]);
memset(mul,,sizeof(mul));
memset(e,,sizeof(e));
for (int i=;i<=n;i++) {
int ALL=<<fac[a[i]].size();
for (int j=;j<ALL;j++) {
int sum=;
for (int k=;k<fac[a[i]].size();k++)
if (j&(<<k)) sum=sum*fac[a[i]][k];
mul[sum]++; //代表是sum倍数的a[i]的个数++
}
}
for (int i=;i<=n;i++) {
int ALL=<<fac[a[i]].size();
for (int j=;j<ALL;j++) {
int sum=,sig=-;
for (int k=;k<fac[a[i]].size();k++)
if (j&(<<k)) sum=sum*fac[a[i]][k],sig*=-;
e[i]+=sig*mul[sum]; //容斥原理求与a[i]不互质的数个数(包括自己)
}
e[i]=n-e[i]; //补集就是与a[i]互质的数个数(不包括自己)
if (a[i]==) e[i]=n-;
} long long ans=,tmp=;
for (int i=n;i>n-;i--) ans=ans*i;
ans=ans/; //计算全集C(n,3) for (int i=;i<=n;i++) tmp+=(long long)(e[i])*(n-e[i]-); //计算异色三角形数量
printf("%lld\n",ans-tmp/);
}
return ;
}

容斥原理+补集转化+MinMax容斥的更多相关文章

  1. [模板] 容斥原理: 二项式反演 / Stirling 反演 / min-max 容斥 / 子集反演 / 莫比乌斯反演

    //待更qwq 反演原理 二项式反演 若 \[g_i=\sum_{j=1}^i {\binom ij} f_j\] , 则有 \[ f_i=\sum_{j=1}^i (-1)^{i-j} {i \ch ...

  2. luoguP3175 [HAOI2015]按位或 min-max容斥 + 高维前缀和

    考虑min-max容斥 \(E[max(S)] = \sum \limits_{T \subset S} min(T)\) \(min(T)\)是可以被表示出来 即所有与\(T\)有交集的数的概率的和 ...

  3. BZOJ4036 [HAOI2015]按位或 【minmax容斥 + 期望 + FWT】

    题目链接 BZOJ4036 题解 好套路的题啊,,, 我们要求的,实际上是一个集合\(n\)个\(1\)中最晚出现的\(1\)的期望时间 显然\(minmax\)容斥 \[E(max\{S\}) = ...

  4. Min-Max容斥及其推广和应用

    概念 Min-Max容斥,又称最值反演,是一种对于特定集合,在已知最小值或最大值中的一者情况下,求另一者的算法. 例如: $$max(a,b)=a+b-min(a,b) \\\ max(a,b,c)= ...

  5. [luogu 3175] [HAOI2015]按位或(min-max容斥+高维前缀和)

    [luogu 3175] [HAOI2015]按位或 题面 刚开始你有一个数字0,每一秒钟你会随机选择一个[0,2^n-1]的数字,与你手上的数字进行按位或运算.问期望多少秒后,你手上的数字变成2^n ...

  6. bzoj4036 [HAOI2015]按位或 状压DP + MinMax 容斥

    题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4036 题解 变成 \(2^n-1\) 的意思显然就是每一个数位都出现了. 那么通过 MinMa ...

  7. min-max容斥学习笔记

    min-max容斥学习笔记 前置知识 二项式反演 \[ f(n)=\sum_{i=0}^n\binom{n}{i}g(i)\Leftrightarrow g(n)=\sum_{i=0}^n(-1)^{ ...

  8. [总结] Min-Max容斥学习笔记

    min-max 容斥 给定集合 \(S\) ,设 \(\max(S)\) 为 \(S\) 中的最大值,\(\min(S)\) 为 \(S\) 中的最小值,则: \[\max(S)=\sum_{T\in ...

  9. 「PKUWC2018」随机游走(min-max容斥+FWT)

    「PKUWC2018」随机游走(min-max容斥+FWT) 以后题目都换成这种「」形式啦,我觉得好看. 做过重返现世的应该看到就想到 \(min-max\) 容斥了吧. 没错,我是先学扩展形式再学特 ...

随机推荐

  1. spring cloud学习笔记三 Feign与Ribbon负载均衡的区别

    一.Feign的介绍 Feign一般比较书面的解释是:Feign是一个声明式的WebService客户端,使用Feign编写的WebService客户端更加简单,他的使用方法是定义一个接口,然后在上线 ...

  2. Ehcahe独立使用

    <?xml version="1.0" encoding="utf-8"?><ehcache xmlns:xsi="http://w ...

  3. vue自定义抽屉组件

    <template> <div class="drawer"> <div :class="maskClass" @click=&q ...

  4. mobx学习笔记01——什么是mobx?

    mobx是什么? js框架 官方定义:Simple,scalable state management(简单.可扩展的状态管理) mobx与redux相比: 语义丰富.响应式编程,开发难度低.学习成本 ...

  5. 本地develop往远端develop上推代码步骤

  6. 4412 GPIO读 和 ioremap控制GPIO寄存器

    一.配置GPIO读 在视频14的基础上做 1.利用拨码开关来实现GPIO输入 所以AP_SLEEP对应GPC0_3,然后在drivers/gpio/gpio-exynos4.c中对应EXYNOS4_G ...

  7. linux 基础命令总结

    1.mkdir 创建目录 -p 创建多级目录 mkdir -p /data/test -m, --mode=模式 设置权限模式(类似chmod),而不是rwxrwxrwx 减umask -p, --p ...

  8. BUUCTF | [CISCN2019 华北赛区 Day2 Web1]Hack World

    id=0 id=1 id=2 id=3 发现结果不一样,尝试 : ">4","=4","<4" : 在自己的环境下验证一下: 爆 ...

  9. Xcode cannot run using the selected device after upgrade

    Please follow below step 1>Go to Project Build setting 2>Change compiler for c/c++/objective c ...

  10. Gym-100676E Time Limit Exceeded?

    原题链接 https://odzkskevi.qnssl.com/1110bec98ca57b5ce6aec79b210d2849?v=1491063604 ********************* ...