这题是2016 CCPC 东北四省赛的B题, 其实很简单. 现场想到的就是正解, 只是在合并两个并查集这个问题上没想清楚.

做法

并查集合并 + 归并

  1. 对每个节点 $u$, 将 $u$ 到根的那些边添到一个初始为空的并查集中, 得到的并查集记作 $a_u$.
  2. 询问相当于将 $k$ 个并查集合并. 采用二路归并, 合并次数是 $O(n \cdot \log(n))$.

    $ n/2 + n/4 + n/8 + \dots + 1 = O(n \cdot \log(n)) $

合并两个并查集

详细讨论将并查集 $B$ 合并到并查集 $A$ 中这一问题.

这个问题与

给定两无向图 $A, B, V_B \subset V_A; \quad A(E_A, V_A) \to A'( E_A, E_A \cup E_B) $.

等价.

做法

$ \forall u \in E_B, \quad A.\mathrm{unite}(u, B.\mathrm{root}(u)) $

正确性

只要验证

在$B$中连通的任意两点 $u, v$, 在$ A'$中也连通.

是否满足.

Implementation

#include <bits/stdc++.h>
using namespace std; const int N{1<<9};
const int M=1e4+5; int n, m; struct DSU{
int par[N];
int cnt; int find(int x){
return par[x]==x?x: par[x]=find(par[x]);
} void unite(int x, int y){
x=find(x);
y=find(y);
if(x!=y){
par[x]=y;
--cnt;
}
} void unite(DSU &a){
for(int i=1; i<=n; i++){
unite(find(i), a.find(i)); // ?
}
} void init(){
for(int i=1; i<=n; i++){
par[i]=i;
}
cnt=n;
} void copy(const DSU &a){
for(int i=1; i<=n; i++){
par[i]=a.par[i];
}
cnt=a.cnt;
}
}; DSU a[M], b[M]; vector<int> g[M]; struct Edge{
int u, v;
void read(){
scanf("%d%d", &u, &v);
}
}E[M]; void dfs(int u, int f){
a[u].copy(a[f]);
a[u].unite(E[u].u, E[u].v); for(auto v: g[u]){
dfs(v, u);
}
} void solve(int n){
for(int i=1; i<n; i<<=1){ // error-prone
for(int j=0; j+i<n; j+=i<<1){
b[j].unite(b[j+i]);
}
}
printf("%d\n", b[0].cnt);
} // int par[M]; int main(){ int T, cas{};
for(cin>>T; T--; ){
printf("Case #%d:\n", ++cas);
// int n, m;
cin>>n>>m; for(int i=1; i<=m; ++i){
g[i].clear();
} for(int i=2; i<=m; i++){
// scanf("%d", par+i);
int fa;
scanf("%d", &fa);
g[fa].push_back(i);
} for(int i=1; i<=m; ++i){
E[i].read();
} a[0].init();
dfs(1, 0); int q;
cin>>q;
for(; q--; ){
int k;
scanf("%d", &k);
for(int i=0; i<k; i++){
int x;
scanf("%d", &x);
b[i].copy(a[x]);
}
solve(k);
}
}
return 0;
}

Pitfalls

归并

for(int i=1; i<n; i<<=1){   // error-prone
for(int j=0; j+i<n; j+=i<<1){
b[j].unite(b[j+i]);
}
}

容易写错.

我第一发是这样写的

for(int i=2; i<=n; i<<=1){
for(int j=0; j+i/2<n; j+=i){
b[j].unite(b[j+i/2]);
}
}

n==3时, 只做了1轮归并.

应采纳第一种写法, 很清楚.


UPD

太SB了.

  1. 根本不用归并, 直接逐个合并就好了.
  2. 根本不用 b[i].copy(a[x]); , 只要从一个边集为空的图 (以下简称"空图") 开始, 不断把$k$个并查集合并进去就好了.
  3. 不从空图开始, 而从某个并查集开始, 会快很多.
#include <bits/stdc++.h>
using namespace std; const int N{1<<9};
const int M=1e4+5; int n, m; struct DSU{
int par[N];
int cnt; int find(int x){
return par[x]==x?x: par[x]=find(par[x]);
} void unite(int x, int y){
x=find(x);
y=find(y);
if(x!=y){
par[x]=y;
--cnt;
}
} void unite(DSU &a){
for(int i=1; i<=n; i++){
unite(find(i), a.find(i)); // ?
}
} void init(){
for(int i=1; i<=n; i++){
par[i]=i;
}
cnt=n;
} void copy(const DSU &a){
for(int i=1; i<=n; i++){
par[i]=a.par[i];
}
cnt=a.cnt;
}
}; DSU a[M], b[M]; vector<int> g[M]; struct Edge{
int u, v;
void read(){
scanf("%d%d", &u, &v);
}
}E[M]; void dfs(int u, int f){
a[u].copy(a[f]);
a[u].unite(E[u].u, E[u].v); for(auto v: g[u]){
dfs(v, u);
}
} int solve(int n){
if(k==0){
return n;
}
int x;
scanf("%d", &x);
a[0].copy(a[x]);
for(int i=1; i<n; i++){
scanf("%d", &x);
a[0].unite(a[x]);
}
return a[0].cnt;
} int main(){ int T, cas{};
for(cin>>T; T--; ){
printf("Case #%d:\n", ++cas); cin>>n>>m; for(int i=1; i<=m; ++i){
g[i].clear();
} for(int i=2; i<=m; i++){
// scanf("%d", par+i);
int fa;
scanf("%d", &fa);
g[fa].push_back(i);
} for(int i=1; i<=m; ++i){
E[i].read();
} a[0].init();
dfs(1, 0); int q;
cin>>q;
for(; q--; ){
int k;
scanf("%d", &k);
printf("%d\n", solve(k));
}
}
return 0;
}

HDU 5923 Prediction的更多相关文章

  1. HDU 5923 Prediction(2016 CCPC东北地区大学生程序设计竞赛 Problem B,并查集)

    题目链接  2016 CCPC东北地区大学生程序设计竞赛 B题 题意  给定一个无向图和一棵树,树上的每个结点对应无向图中的一条边,现在给出$q$个询问, 每次选定树中的一个点集,然后真正被选上的是这 ...

  2. HDU 1338 Game Prediction

    http://acm.hdu.edu.cn/showproblem.php?pid=1338 Problem Description Suppose there are M people, inclu ...

  3. HDU 1338 Game Prediction【贪心】

    解题思路: 给出 n  m 牌的号码是从1到n*m 你手里的牌的号码是1到n*m之间的任意n个数,每张牌都只有一张,问你至少赢多少次 可以转化为你最多输max次,那么至少赢n-max次 而最多输max ...

  4. HDU——PKU题目分类

    HDU 模拟题, 枚举1002 1004 1013 1015 1017 1020 1022 1029 1031 1033 1034 1035 1036 1037 1039 1042 1047 1048 ...

  5. HDU 5925 Coconuts 【离散化+BFS】 (2016CCPC东北地区大学生程序设计竞赛)

    Coconuts Time Limit: 9000/4500 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Su ...

  6. hdu 5895 广义Fibonacci数列

    Mathematician QSC Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Othe ...

  7. HDOJ 2111. Saving HDU 贪心 结构体排序

    Saving HDU Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total ...

  8. 【HDU 3037】Saving Beans Lucas定理模板

    http://acm.hdu.edu.cn/showproblem.php?pid=3037 Lucas定理模板. 现在才写,noip滚粗前兆QAQ #include<cstdio> #i ...

  9. hdu 4859 海岸线 Bestcoder Round 1

    http://acm.hdu.edu.cn/showproblem.php?pid=4859 题目大意: 在一个矩形周围都是海,这个矩形中有陆地,深海和浅海.浅海是可以填成陆地的. 求最多有多少条方格 ...

随机推荐

  1. flex布局模式简单概述

    CSS3中新增一种弹性布局模型:flexbox.网上关于flex的介绍很多,这里介绍下常用的几个属性.弹性布局的特点是非常灵活.可根据剩余的宽高,灵活布局. 先用图片说明flex具有哪些属性.(网上盗 ...

  2. 前端科普文—为什么<!DOCTYPE> 不可或缺

    When question comes 你一定在 HTML 页面最前面看到过这样一行代码(比如 百度): <!DOCTYPE html> 或者说类似这样的(比如 博客园-韩子迟 PS:博客 ...

  3. IntelliJ IDEA,代码行宽度超出限制时自动换行

    转自:http://my.oschina.net/angerbaby/blog/471351 当我们使用IDE写代码时,为了保证代码的可阅读性和优雅性,通常会借助IDE的代码风格设置功能,令IDE智能 ...

  4. Android开发遇到的坑(1):Java中List的安全删除问题

    在项目的开发过程中,一定少不了的是对Java集合中的List接触.项目中对List的删掉也是一种常见的操作,看上这个操作也没什么好说的样子,但是在项目开发中也是最容易出错的地方,特别是对于新手.有时候 ...

  5. 学习服务端JavaScript这个有名的Node.js

    没接触过,试着简单学一下,从头开始: 参照这个教程:https://github.com/alsotang/node-lessons/tree/master/lesson0 一.搭建环境: 1.搭建N ...

  6. C# Gabbage Collecting System

    * 这个程序非常巧妙的探测了一下垃圾回收机制,发现如下结论: * 当内存紧急时,才会启动垃圾回收GC.Collect() * 从此程序的运行上来看,delete是连续出现的,这体现了垃圾回收的强度. ...

  7. 【JavaEE企业应用实战学习记录】struts配置文件详细解析

    <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-/ ...

  8. Beta版本冲刺———第四天

    会议照片: 项目燃尽图: 1.项目进展: 今天解决的进度:新增加了一个撤销按钮,实现对上一步操作的撤销. 仍在进行对排行榜分数变更的实现. 2.每个人每天做的事情 郭怡锋:汇总工作进度,对此总结,进行 ...

  9. Linux IO Scheduler(Linux IO 调度器)

    每个块设备或者块设备的分区,都对应有自身的请求队列(request_queue),而每个请求队列都可以选择一个I/O调度器来协调所递交的request.I/O调度器的基本目的是将请求按照它们对应在块设 ...

  10. poj1523 求割点 tarjan

    SPF Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 7678   Accepted: 3489 Description C ...