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 题目大意: 在一个矩形周围都是海,这个矩形中有陆地,深海和浅海.浅海是可以填成陆地的. 求最多有多少条方格 ...
随机推荐
- 前端见微知著番外篇:Bitbucket进行代码管控
说道代码管控,一般都会提到TFS.Git等,但是在这里我们将要用到Bitbucket,其实其操作方式和Git基本上一样,但是和TFS则有很大的不同了.但是原理基本上都是一致的. 这里我不会过多的涉及到 ...
- Redis百亿级Key存储方案
1 需求背景 该应用场景为DMP缓存存储需求,DMP需要管理非常多的第三方id数据,其中包括各媒体cookie与自身cookie(以下统称supperid)的mapping关系,还包括了supperi ...
- 基于FPGA的通信系统实验
伪随机信号发生器 1.伪随机信号发生器原理 伪随机信号发生器又叫PN序列发生器或者是m序列发生器.m序列是一种线性反馈寄存器序列,m序列的产生可以利用r级寄存器产生长度为2^r-1的m序列,该实验中采 ...
- [BZOJ2730][HNOI2012]矿场搭建(求割点)
题目:http://www.lydsy.com:808/JudgeOnline/problem.php?id=2730 分析: 如果坍塌的点不是割点,那没什么影响,主要考虑坍塌的点是割点的情况. 显然 ...
- htop查看系统负载
htop 是 Linux 系统中的一个互动进程查看器,可以让用户进行交互式操作,可横向或纵向滚动浏览进程列表,支持鼠标操作.用户可以在安装 htop 来监控服务器的负载. 01.下载 https:// ...
- 使用js和jq去掉左右空格方法
<!DOCTYPE html><html> <head> <meta charset="UTF-8"> <script src ...
- 网页中常用HTML字符实体
摘要: 一些字符在 HTML 中拥有特殊的含义,比如小于号 () 用于定义 HTML 标签的开始.如果我们希望浏览器正确地显示这些字符,我们必须在 HTML 源码中插入字符实体. 字符实体有三部分:一 ...
- [转] JPQL
原文地址:http://blog.csdn.net/suncaishen/article/details/6512028 select name ,age from user; //原生SQL语句 s ...
- jsp内置对象作业2-留言簿
1.留言簿页面:liuYan.jsp <%@ page language="java" contentType="text/html; charset=UTF-8& ...
- nginx的安装
1,,nginx的安装 为什么使用nginx我就不多说了,很优秀的,请再行google 在安装前,先安装pcre,安装zlib,安装openssl,以及一些其他包 yum install -y gcc ...