容斥原理+补集转化+MinMax容斥
容斥原理的思想大家都应该挺熟悉的,然后补集转化其实就是容斥原理的一种应用。
一篇讲容斥的博文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容斥的更多相关文章
- [模板] 容斥原理: 二项式反演 / 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 ...
- luoguP3175 [HAOI2015]按位或 min-max容斥 + 高维前缀和
考虑min-max容斥 \(E[max(S)] = \sum \limits_{T \subset S} min(T)\) \(min(T)\)是可以被表示出来 即所有与\(T\)有交集的数的概率的和 ...
- BZOJ4036 [HAOI2015]按位或 【minmax容斥 + 期望 + FWT】
题目链接 BZOJ4036 题解 好套路的题啊,,, 我们要求的,实际上是一个集合\(n\)个\(1\)中最晚出现的\(1\)的期望时间 显然\(minmax\)容斥 \[E(max\{S\}) = ...
- Min-Max容斥及其推广和应用
概念 Min-Max容斥,又称最值反演,是一种对于特定集合,在已知最小值或最大值中的一者情况下,求另一者的算法. 例如: $$max(a,b)=a+b-min(a,b) \\\ max(a,b,c)= ...
- [luogu 3175] [HAOI2015]按位或(min-max容斥+高维前缀和)
[luogu 3175] [HAOI2015]按位或 题面 刚开始你有一个数字0,每一秒钟你会随机选择一个[0,2^n-1]的数字,与你手上的数字进行按位或运算.问期望多少秒后,你手上的数字变成2^n ...
- bzoj4036 [HAOI2015]按位或 状压DP + MinMax 容斥
题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4036 题解 变成 \(2^n-1\) 的意思显然就是每一个数位都出现了. 那么通过 MinMa ...
- min-max容斥学习笔记
min-max容斥学习笔记 前置知识 二项式反演 \[ f(n)=\sum_{i=0}^n\binom{n}{i}g(i)\Leftrightarrow g(n)=\sum_{i=0}^n(-1)^{ ...
- [总结] Min-Max容斥学习笔记
min-max 容斥 给定集合 \(S\) ,设 \(\max(S)\) 为 \(S\) 中的最大值,\(\min(S)\) 为 \(S\) 中的最小值,则: \[\max(S)=\sum_{T\in ...
- 「PKUWC2018」随机游走(min-max容斥+FWT)
「PKUWC2018」随机游走(min-max容斥+FWT) 以后题目都换成这种「」形式啦,我觉得好看. 做过重返现世的应该看到就想到 \(min-max\) 容斥了吧. 没错,我是先学扩展形式再学特 ...
随机推荐
- Oracle 设置自启动
1. 环境准备 1.1 系统 操作系统:CentOS 7(64位) 1.2 工具/软件 已安装完成的Oracle11g(64位): 创建数据库实例,本文中数据库实例名:test:$ORACLE_SI ...
- bzoj4244 & loj2878. 「JOISC 2014 Day2」邮戳拉力赛 括号序列+背包
题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4244 https://loj.ac/problem/2878 题解 挺妙的一道题. 一开始一直 ...
- JSP相关学习
动态页面技术(JSP/EL/JSTL) <!-- jsp的三种脚本方式 --> <% int i = 5; //这是单行注释 /*这是多行注释*/ %> <%=i%> ...
- 正确读取resources目录下的文件
问题描述:本地可以正常读取areacode.json文件,打成jar包在测试环境找不到该文件. 问题代码: static { StringBuffer strbuffer = new StringBu ...
- Raspbian 在虚拟机上运行,运行Flask,供宿主机访问
Raspbian 在虚拟机上运行,启动Flask,供宿主机访问 参考ref 1, 在virtualbox上跑起来Raspbian OS 参考ref 2, 在Raspbian上安装并运行Falsk, 注 ...
- 2019 GNTC 阿里云参会分享:云原生SDWAN网络2.0 一站式上云服务
本次10/22-24 南京2019 GNTC大会上,阿里云网络云原生SDWAN网络2.0 由于独特的云原生定位.创新的解决方案,及成熟的应用案例.行业用户,获得行业媒体C114中国通信网.产业专家高度 ...
- npm 常见错误记录
1.Module build failed: ReferenceError: Unknown plugin "import" specified in "base&quo ...
- shell脚本学习(5)join
join 不是简单的把两个文本连接起来 sale.txt quotas.txt
- javascript中new关键字详解
和其他高级语言一样 javascript 中也有 new 运算符,我们知道 new 运算符是用来实例化一个类,从而在内存中分配一个实例对象. 但在 javascript 中,万物皆对象,为什么还要通过 ...
- 【Linux】运维常用命令
1.查看进程 ps -ef 如果需要查看特定的进程,比如redis的 ps -ef | grep redis 2.强制杀死进程 kill -9 进程id 3.忽略输出后台启动 nohup ./red ...