参考博客了博客 同时也参考了cf评论中humbertoyusta的题解

然后详细的对它的最后一步进行了一个证明

先说下题意:
给你两个结点数相同的2个森林它有n个点,你每次同时给两个森林的同一对结点连边,使得这两个森林最后还是两个森林(森林:1~n棵树组成),问你最多加哪几条边。

解析下:
你能加的边数 = min(n - 1 - m1, n - 1 - m2)
因为加到最后两个森林的中至少会有一个变成一棵树,这棵树结点数是n,那么它的边数就是 n - 1。你每次选择哪一个结点去加边实际上不影响你最多可以加几条边,因为极限是其中1个森林变成一棵树也就是只有一个联通块,所以不管你怎么加边,只要两个森林都是有两个或两个以上联通块你总能找出一个连边的方法(可以自己去证明下) 从而达到只有一个联通块的状态(只有一棵树),而这个过程中森林的边数的变化还是从max(m1,m2)到 n - 1,也就是min(n - 1 - m1, n - 1 - m2);所以不论过程如何,最后可以加的最多的边数是不变的。

既然如此不妨先尽可能的给一个点加边,从而减小联通块数量

在保证森林1和森林2中的所有树还是树的前提下。你可以试着对每一个结点x建立一个(1,x)的边。

假设这是原图

进行上述操作后就是这样了

之后你会发现对结点1已经没有可以操作的余地了,你只能考虑除了结点1的其它联通块了。然后你就会发现这两个森林除1号结点联通块外的其它联通块之间都是互不联通的,比如上图森林1的5号结点和森林2的,2,3结点在各自森林里不连通,6号结点也和森林2的2,3结点在各自森林里不连通。

所以直接从两个森林除了1号结点的联通块的其它联通块中各自选出一个两两建边连接起来就行了

那么以上发现具有一般性吗?证明如下:

定义2个集合分别为s1,s2分别表示森林1和森林2中除结点编号1所在的树以外其它树的根结点编号,也就是各自森林除了结点1外的联通块集合。

如上图,对应的s1,s2分别为下图所示

那么它有如下两个性质
下面证明中树就是联通块

①这两个集合内不存在相同结点。反证: 如果有,因为s1,s2集合中的点和结点1不联通,所以这两个相同结点x的树一定可以各自建立一条边(1,x)与1号结点所对应的树联通,那么根据对这两个集合的定义,既然结点x和结点1联通了,就自然不会出现在集合s1,s2中。

如下图

②对于s2的任意一个根结点b对应在森林1的b结点一定在1号树上。反证:如果不在,那么就说明s1的树a上有一个b号结点它与森林1的1号树不联通,森林2中的b也和森林2的1号树不联通,那么他们一定可以通过之前的操作和建立边(b,1)从而与1号结点所对应的树联通,既然结点b和结点1联通了,就自然不会出现在集合s1,s2中。

如下图

再陈述下上面得两个性质
①s1中结点编号和s2中得结点编号一定都不相同。②集合s2中的任何一个结点一定不在集合s1中的任何一个结点对应的树上,而是在对应森林里的1号结点的树上,反之亦然。也就是集合s1,和s2上的任意两点在各自森林都不联通。所以可以大胆的对这两个点加边,并且使得结点与各自森林的1号树联通。

所以你每次可以在集合s1,s2中各自取出一个结点,放心的对森林1和森林2各自将这两点连接。

如下图

时间复杂度分析:
①每个森林对结点1的连边操作(1,x) ,用并查集维护联通块 O(nlogn)
②求出除了1号结点所在联通块的其它联通块O(n)

代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int ans[N][2];
int fa1[N];
int fa2[N];
int s1[N],s2[N];int find(int fa[],int x){return x == fa[x] ? x : fa[x] = find(fa,fa[x]);
}void merge(int fa[],int x,int y){int fx = find(fa,x);int fy = find(fa,y);fa[fx] = fy;
}void init(int fa[],int n){for(int i = 1 ;i <= n; ++i)fa[i] = i;
}int main(){/*  freopen("in.txt","r",stdin);freopen("out.txt","w",stdout);   */int n,m1,m2;    scanf("%d %d %d",&n,&m1,&m2);init(fa1,n);init(fa2,n);for(int i = 1; i <= m1; ++i){int u,v;    scanf("%d %d",&u,&v);merge(fa1,u,v);}for(int i = 1; i <= m2; ++i){int u,v;    scanf("%d %d",&u,&v);merge(fa2,u,v);}int ct = 0;for(int i = 2; i <= n; ++i){if(find(fa1,1) != find(fa1,i) && find(fa2,1) != find(fa2,i)){ans[++ct][0] = 1;ans[ct][1] = i;merge(fa1,i,1);merge(fa2,i,1);}}int sz1 = 0,sz2 = 0;for(int i = 2 ; i <= n; ++i){if(fa1[i] == i && fa1[i] != find(fa1,1))s1[sz1++] = i;if(fa2[i] == i && fa2[i] != find(fa2,1))s2[sz2++] = i;}for( int i = 0,j = 0; i < sz1 && j < sz2; ++i, ++j ){ans[++ct][0] = s1[i];ans[ct][1] = s2[j];}printf("%d\n",ct);for(int i = 1; i <= ct; ++i)printf("%d %d\n",ans[i][0],ans[i][1]);return 0;
}

D2. Mocha and Diana (Hard Version)相关推荐

  1. codeforces1559 D2. Mocha and Diana (Hard Version)(并查集+启发式合并+随机化)

    D2. Mocha and Diana (Hard Version) RunningBeef题解 首先将图1的点与1号点所在的连通块相连,图2类似. 然后就是在图1和图2中选择没有和1号点在同一个连通 ...

  2. CodeForces - 1559D2 Mocha and Diana (Hard Version)(思维)

    题目链接:点击查看 题目大意:给出两棵森林,每次可以同时在两个森林中增加同一条边,问最多可以增加多少条边,使得两个森林仍然还是森林 题目分析:结论参考至:https://blog.csdn.net/R ...

  3. Mocha and Diana (Easy Version) 并查集维护两片森林

    题意 : 给两片n个节点的森林,每次分别在两片森林的u和v节点之间加一条边,要求仍然是两片森林,求最多能加多少条边. 1≤

  4. D1. Coffee and Coursework (Easy version) and D2. Coffee and Coursework (Hard Version)

    http://codeforces.com/contest/1118/problem/D1 http://codeforces.com/contest/1118/problem/D2 题意:有n杯咖啡 ...

  5. D2. Coffee and Coursework (Hard Version)(思维+贪心)

    https://codeforces.com/problemset/problem/1118/D2 思路: 第一眼能看出答案能二分把.然后这题和前缀和不同,虽然每次减的是固定的,但是前缀先加起来会减出 ...

  6. codeforces(D2. Coffee and Coursework (Hard Version))二分答案

    这题一看很容易想到二分,但我一开始想偏了,我是想枚举天数,然后二分去验证天数是否满足,但是这样二分验证天数是否满足这一步卡住了,写不出来. 然后我改成二分天数,验证天数是否满足这一步改成暴力计算,这样 ...

  7. Codeforces Round #738 (Div. 2)

    Codeforces Round #738 (Div. 2) 文章目录 A 题解: 代码: B 题意: 题解: 代码: C 题意: 题解: 代码: D1 题意: 题解: 代码: 题号 题目 知识点 A ...

  8. codeforces:ProblemMset

    最近一个月在codeforces上做的题(做个记录) 后面太多了就不把代码一一放出了,只放置了链接,可根据链接找到提交的代码. 最小子矩阵 #include <iostream> #inc ...

  9. CF杂题训练(交互题不做,2500以上的看情况吧)

    CF专练 CF1562 A The Miracle and the Sleeper B Scenes From a Memory C Rings D Two Hundred Twenty One E ...

最新文章

  1. [NOI2012]骑行川藏
  2. Ng-template寄宿方式
  3. Linux shell脚本中单双引号的区别
  4. http://hudeyong926.iteye.com/blog/977152
  5. vue --- 使用animate.css实现动画
  6. sqli-lab--writeup(7~10)文件输出,时间布尔盲注
  7. python multiprocessing.Process 应用
  8. 啤酒与尿布,咩叔原创基于图论简单到爆的实时关联性算法
  9. [论文阅读] Learning Loss for Active Learning
  10. 天价部队到老家赶来java作文_天价与廉价作文800字
  11. 关于np.meshgrid
  12. 电驴创始人Jed McCaleb的传奇人生
  13. 使用QXDM Log 來分析LTE環境資訊
  14. dcs world f15c教学_烟台TSXP57353M【四点零自动化】DCS系统
  15. MFC中SetTimer函数
  16. 新手做国外广告联盟lead常用工具汇总!
  17. 韩立刚计算机网络——第三章:数据链路层
  18. python使用ffmpeg合并两个MP4视频
  19. 福建农林大学校赛(同步赛)
  20. sigset 与 signal的区别?

热门文章

  1. JS简单实现进度条效果
  2. 2022年中国一体化压铸行业概览:*引领潮流,*或成资本新宠
  3. joomla模板开发
  4. error Expected linebreaks to be 'CRLF' but found 'LF' linebreak-style
  5. 计算机能否代替传统教学设备,电子教案能否取代传统教案?
  6. 小米电视自带app内的电影评分系统
  7. android 存储容量 工具,AndroidStudio分析工具
  8. 计算机插入U盘意外重启,VMware虚拟机不能启动
  9. PHP 打印支票,HTML_用Access2007解决支票打印的日期大写问题,财务人员都知道,转账支票的 - phpStudy...
  10. python压缩视频文件_python 批量压缩手机视频