这题是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. 资源搜集:Git精品文章推荐,常年更新

    以下放置的是搜集到的Git精品文章,常年更新: Git 常用命令详解(二) Git版本控制软件结合GitHub从入门到精通常用命令学习手册 Pro Git(中文版)

  2. 【深圳】OSC源创会第44期 开始报名

    时间:2016-03-19 14:00 地点: 深圳 南山区海德三道天利中央商务广场B座负一楼(意启创业) 费用:50元/人(现场交),女士.50积分的账号.开源软件作者.学生免费 (用于准备茶歇小食 ...

  3. 数据字典生成工具之旅(6):NVelocity语法介绍及实例

    本章开始将会为大家讲解NVelocity的用法,并带领大家实现一个简单的代码生成器. NVelocity是一个基于.NET的模板引擎(template engine).它允许任何人仅仅简单的使用模板语 ...

  4. Bootstrap系列 -- 1. 如何使用Bootstrap

    一. Bootstrap 简介 Bootstrap 是一个前端框架,使用Bootstrap可以做出很多漂亮的页面,中文官网:http://www.bootcss.com/ 二. Bootstrap核心 ...

  5. Bootstrap系列 -- 43. 固定导航条

    很多情况之一,设计师希望导航条固定在浏览器顶部或底部,这种固定式导航条的应用在移动端开发中更为常见.Bootstrap框架提供了两种固定导航条的方式:  .navbar-fixed-top:导航条固定 ...

  6. js中的forEach

    chrome和firefox支持数组的forEach,但不支持对象的forEach,IE啥都不支持 jquery中的$.each(ArrayOrObject,function)既可以遍历数组又可以遍历 ...

  7. C++命名规范

    1.1    类型名 首字母大写,末尾加_T.如: class TnppCoverageArea_T{…}; 1.2    1.2 变量和函数名 变量和函数名中首字母小写,其后每个英文单词的第一个字母 ...

  8. SpringMVC学习--参数绑定

    spring参数绑定过程 从客户端请求key/value数据,经过参数绑定,将key/value数据绑定到controller方法的形参上.springmvc中,接收页面提交的数据是通过方法形参来接收 ...

  9. 给Linux装图形化界面

    在工作中遇到这种情况,安装confluence服务的时候需要调用图形化桌面,但是原来装系统的时候是最小化装的,所以这里就麻烦了 给装图形化吧 在我们安装Linux系统时,刚开始的时候可能没有安装图形界 ...

  10. Query DSL for elasticsearch Query

    Query DSL Query DSL (资料来自: http://www.elasticsearch.cn/guide/reference/query-dsl/) http://elasticsea ...