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 题目大意: 在一个矩形周围都是海,这个矩形中有陆地,深海和浅海.浅海是可以填成陆地的. 求最多有多少条方格 ...
随机推荐
- Nginx+Varnish 实现动静分离,为服务器分流,降低服务器负载
相必大家在看加快网站响应速度方面的文章时,都提过这么一条:动静分离.那怎样实现动静分离呢,这里笔者就亲自搭建相关服务实现动静分离. 动静分离是一种架构,就是把静态文件,比如JS.CSS.图片甚至有些静 ...
- 理解JavaScript的作用域链
上一篇文章中介绍了Execution Context中的三个重要部分:VO/AO,scope chain和this,并详细的介绍了VO/AO在JavaScript代码执行中的表现. 本文就看看Exec ...
- 谈谈关于Python里面小数点精度控制的问题
基础 浮点数是用机器上浮点数的本机双精度(64 bit)表示的.提供大约17位的精度和范围从-308到308的指数.和C语言里面的double类型相同.Python不支持32bit的单精度浮点数.如果 ...
- matlab 画图中线型及颜色设置
matlab受到控制界广泛接受的一个重要原因是因为它提供了方便的绘图 功能.本章主要介绍2维图形对象的生成函数及图形控制函数的使用方 法,还将 简单地介绍一些图形的修饰与标注函数及操作和控制MATLA ...
- [转]7个高性能JavaScript代码高亮插件
对于喜欢写技术博客的同学来说,一定对代码高亮组件非常熟悉.一款优秀的JavaScript代码高亮插件,将会帮助你渲染任何一种编程语言,包括一些关键字的着色,以及每行代码的缩进等.今天我们要来分享一些高 ...
- 关于提高python程序执行效率的思路和想法
相比编译型语言(C系列)python胜在简介的语法和优雅的动态编程体验,但是在执行效率上,python有解释性语言先天的劣势——执行效率较低,为了让编写出的程序获得更快的执行效率,开启此文章. pyt ...
- 浅谈SEO翻倍提升网站流量
本文是在简单了解并且熟悉SEO技术的基础上加上自己的总结归纳,对于SEO还是一个长期的工程不是一天就能一下优化排名第一.下面就是我对SEO的一些见解: 一.关键字选择 内容相关 搜索次数多,竞争小 主 ...
- Java GC收集器配置说明
根据Java GC收集器具体分类,我们可以看出JVM根据需求不同提供了三种选择:串行收集器.并行收集器.并发收集器. 串行收集器只适用于小数据量的情况,我们主要了解一下并行收集器和并发收集器.默认情况 ...
- 多词查询(Multi-word Queries)
如果我们一次只能搜索一个词,那么全文搜索就会显得相当不灵活.幸运的是,通过match查询来实现多词查询也同样简单: GET /my_index/my_type/_search { "quer ...
- 第一天的作业,登录接口脚本 login.py
user_list = [] count = 0 user = "liruixin" password = " raw_user = raw_input("us ...