HDU 5923 Prediction
这题是2016 CCPC 东北四省赛的B题, 其实很简单. 现场想到的就是正解, 只是在合并两个并查集这个问题上没想清楚.
做法
并查集合并 + 归并
- 对每个节点 $u$, 将 $u$ 到根的那些边添到一个初始为空的并查集中, 得到的并查集记作 $a_u$.
- 询问相当于将 $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了.
- 根本不用归并, 直接逐个合并就好了.
- 根本不用
b[i].copy(a[x]);
, 只要从一个边集为空的图 (以下简称"空图") 开始, 不断把$k$个并查集合并进去就好了. - 不从空图开始, 而从某个并查集开始, 会快很多.
#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的更多相关文章
- HDU 5923 Prediction(2016 CCPC东北地区大学生程序设计竞赛 Problem B,并查集)
题目链接 2016 CCPC东北地区大学生程序设计竞赛 B题 题意 给定一个无向图和一棵树,树上的每个结点对应无向图中的一条边,现在给出$q$个询问, 每次选定树中的一个点集,然后真正被选上的是这 ...
- HDU 1338 Game Prediction
http://acm.hdu.edu.cn/showproblem.php?pid=1338 Problem Description Suppose there are M people, inclu ...
- HDU 1338 Game Prediction【贪心】
解题思路: 给出 n m 牌的号码是从1到n*m 你手里的牌的号码是1到n*m之间的任意n个数,每张牌都只有一张,问你至少赢多少次 可以转化为你最多输max次,那么至少赢n-max次 而最多输max ...
- HDU——PKU题目分类
HDU 模拟题, 枚举1002 1004 1013 1015 1017 1020 1022 1029 1031 1033 1034 1035 1036 1037 1039 1042 1047 1048 ...
- HDU 5925 Coconuts 【离散化+BFS】 (2016CCPC东北地区大学生程序设计竞赛)
Coconuts Time Limit: 9000/4500 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Su ...
- hdu 5895 广义Fibonacci数列
Mathematician QSC Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Othe ...
- HDOJ 2111. Saving HDU 贪心 结构体排序
Saving HDU Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total ...
- 【HDU 3037】Saving Beans Lucas定理模板
http://acm.hdu.edu.cn/showproblem.php?pid=3037 Lucas定理模板. 现在才写,noip滚粗前兆QAQ #include<cstdio> #i ...
- hdu 4859 海岸线 Bestcoder Round 1
http://acm.hdu.edu.cn/showproblem.php?pid=4859 题目大意: 在一个矩形周围都是海,这个矩形中有陆地,深海和浅海.浅海是可以填成陆地的. 求最多有多少条方格 ...
随机推荐
- flex布局模式简单概述
CSS3中新增一种弹性布局模型:flexbox.网上关于flex的介绍很多,这里介绍下常用的几个属性.弹性布局的特点是非常灵活.可根据剩余的宽高,灵活布局. 先用图片说明flex具有哪些属性.(网上盗 ...
- 前端科普文—为什么<!DOCTYPE> 不可或缺
When question comes 你一定在 HTML 页面最前面看到过这样一行代码(比如 百度): <!DOCTYPE html> 或者说类似这样的(比如 博客园-韩子迟 PS:博客 ...
- IntelliJ IDEA,代码行宽度超出限制时自动换行
转自:http://my.oschina.net/angerbaby/blog/471351 当我们使用IDE写代码时,为了保证代码的可阅读性和优雅性,通常会借助IDE的代码风格设置功能,令IDE智能 ...
- Android开发遇到的坑(1):Java中List的安全删除问题
在项目的开发过程中,一定少不了的是对Java集合中的List接触.项目中对List的删掉也是一种常见的操作,看上这个操作也没什么好说的样子,但是在项目开发中也是最容易出错的地方,特别是对于新手.有时候 ...
- 学习服务端JavaScript这个有名的Node.js
没接触过,试着简单学一下,从头开始: 参照这个教程:https://github.com/alsotang/node-lessons/tree/master/lesson0 一.搭建环境: 1.搭建N ...
- C# Gabbage Collecting System
* 这个程序非常巧妙的探测了一下垃圾回收机制,发现如下结论: * 当内存紧急时,才会启动垃圾回收GC.Collect() * 从此程序的运行上来看,delete是连续出现的,这体现了垃圾回收的强度. ...
- 【JavaEE企业应用实战学习记录】struts配置文件详细解析
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-/ ...
- Beta版本冲刺———第四天
会议照片: 项目燃尽图: 1.项目进展: 今天解决的进度:新增加了一个撤销按钮,实现对上一步操作的撤销. 仍在进行对排行榜分数变更的实现. 2.每个人每天做的事情 郭怡锋:汇总工作进度,对此总结,进行 ...
- Linux IO Scheduler(Linux IO 调度器)
每个块设备或者块设备的分区,都对应有自身的请求队列(request_queue),而每个请求队列都可以选择一个I/O调度器来协调所递交的request.I/O调度器的基本目的是将请求按照它们对应在块设 ...
- poj1523 求割点 tarjan
SPF Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 7678 Accepted: 3489 Description C ...