CodeForces 455C Civilization (并查集+树的直径)
Civilization
题目链接:
http://acm.hust.edu.cn/vjudge/contest/121334#problem/B
Description
Andrew plays a game called "Civilization". Dima helps him.
The game has n cities and m bidirectional roads. The cities are numbered from 1 to n. Between any pair of cities there either is a single (unique) path, or there is no path at all. A path is such a sequence of distinct cities v1, v2, ..., vk, that there is a road between any contiguous cities vi and vi + 1 (1 ≤ i < k). The length of the described path equals to (k - 1). We assume that two cities lie in the same region if and only if, there is a path connecting these two cities.
During the game events of two types take place:
Andrew asks Dima about the length of the longest path in the region where city x lies.
Andrew asks Dima to merge the region where city x lies with the region where city y lies. If the cities lie in the same region, then no merging is needed. Otherwise, you need to merge the regions as follows: choose a city from the first region, a city from the second region and connect them by a road so as to minimize the length of the longest path in the resulting region. If there are multiple ways to do so, you are allowed to choose any of them.
Dima finds it hard to execute Andrew's queries, so he asks you to help him. Help Dima.
Input
The first line contains three integers n, m, q (1 ≤ n ≤ 3·105; 0 ≤ m < n; 1 ≤ q ≤ 3·105) — the number of cities, the number of the roads we already have and the number of queries, correspondingly.
Each of the following m lines contains two integers, ai and bi (ai ≠ bi;1 ≤ ai, bi ≤ n). These numbers represent the road between cities ai and bi. There can be at most one road between two cities.
Each of the following q lines contains one of the two events in the following format:
1xi. It is the request Andrew gives to Dima to find the length of the maximum path in the region that contains city xi (1 ≤ xi ≤ n).
2xiyi. It is the request Andrew gives to Dima to merge the region that contains city xi and the region that contains city yi (1 ≤ xi, yi ≤ n). Note, that xi can be equal to yi.
Output
For each event of the first type print the answer on a separate line.
Sample Input
Input
6 0 6
2 1 2
2 3 4
2 5 6
2 3 2
2 5 3
1 1
Output
4
##题意:
给出n个城市和m条已建的双向道路, 其中互相联通的城市组成一个区域.
接着给出q个操作:
1. 查询城市x所在的区域中最长的简单路径.
2. 合并城市x和y所在的区域(新增一条连接两区域的道路),且要求合并后新区域中的最长的简单路径最短.
##题解:
首先很容易想到城市之间的连通性应该用并查集来维护.
第一次想到的思路是带权并查集(最近正在练):
对应每个点维护其到根节点的距离; 维护当前点作为端点时的最长边和次长边.
鉴于操作2的要求:合并区间时应比较两区域根节点(根节点相连一定是最短的)的最长简单路径,把短的合并到长的上.
(路径压缩时更新到根节点的距离; 合并时更新最长和次长边).
以上思路并没有错误,而且可以完美地处理两种操作.(附上代码:WA on test 2).
WA的原因是:对于原本已建的m条道路,不能按照上述合并操作来进行(因为已建道路的两端点是确定的).
所以对于先建的m条边,要想继续沿用上述思路,必须在操作前先处理每个联通块:找出根节点(尽量平衡),并正确建立其他点的父子关系.
很遗憾,上述操作实现上很繁琐.
实际上,对于每个根结点,要想满足上述限制,那么它的最长边和次长边的差值应该不大于1(尽量平衡).所以无需分别维护最长边和次长边.
此外,每个集合内除根节点以外的节点之间的关系并不重要,只需要知道某个点所在区域的根节点即可完成查询和合并操作.
那么对于先建的m条道路就很好处理了:
先前的思路(带权并查集)必须要找出根节点(即路径最长且尽量平衡的点)并维护各子节点之间的关系.
当忽略子节点之间的关系后:只需要任取联通块中的某个节点作为当前集合的“代表”即可(并将集合内的其余结点直接作为代表的子节点). 至于这个代表是否真的在最长路径上,是否平衡都不需要考虑了.
处理先建的m条边:对于每个联通块做两次dfs找出其直径(任取一点找到离它最远的点,然后以此为起点再找最远点).
dfs的过程中把所有子节点的父亲赋成同一个点.
##代码:
``` cpp
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define LL long long
#define eps 1e-8
#define maxn 301000
#define mod 100000007
#define inf 0x3f3f3f3f
#define IN freopen("in.txt","r",stdin);
using namespace std;
int n,m,q;
int fa[maxn];
int _rank[maxn];
int vis[maxn];
vector g[maxn];
void init_set() {
memset(vis, 0, sizeof(vis));
for(int i=0; i<maxn; i++) {
fa[i] = i;
_rank[i] = 0;
g[i].clear();
}
}
int find_set(int x) {
return fa[x] = (x==fa[x]? x:find_set(fa[x]));
}
void unit_set(int x, int y) {
int fa_x = find_set(x);
int fa_y = find_set(y);
if(_rank[fa_x] < _rank[fa_y])
swap(x,y),swap(fa_x,fa_y);
fa[fa_y] = fa_x;
int len1 = _rank[fa_x]/2 + (_rank[fa_x]&1);
int len2 = _rank[fa_y]/2 + (_rank[fa_y]&1);
_rank[fa_x] = max(len1 + len2 + 1, _rank[fa_x]);
}
int len, p;
void dfs(int u, int cur, int FA, int root) {
fa[u] = root;
if(cur > len) {
p = u; len = cur;
}
int sz = g[u].size();
for(int i=0; i<sz; i++) {
if(FA == g[u][i]) continue;
dfs(g[u][i], cur+1, u, root);
}
}
int main(int argc, char const *argv[])
{
//IN;
while(scanf("%d %d %d", &n,&m,&q) != EOF)
{
init_set();
while(m--) {
int x,y; scanf("%d %d", &x, &y);
g[x].push_back(y);
g[y].push_back(x);
vis[x] = vis[y] = 1;
}
for(int i=1; i<=n; i++) {
if(fa[i]==i && vis[i]) {
len = 0; dfs(i,0,0,i);
len = 0; dfs(p,0,0,i);
_rank[i] = len;
}
}
while(q--) {
int type; scanf("%d", &type);
if(type == 1) {
int x; scanf("%d", &x);
int root = find_set(x);
int ans = _rank[root];
printf("%d\n", ans);
} else {
int x,y; scanf("%d %d", &x, &y);
if(find_set(x) == find_set(y)) continue;
else unit_set(x, y);
}
}
}
return 0;
}
<br/>
<big>
旧思路代码:带权并查集.
wrong answer on test 2.
</big>
``` cpp
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <vector>
#define LL long long
#define eps 1e-8
#define maxn 301000
#define mod 100000007
#define inf 0x3f3f3f3f
#define IN freopen("in.txt","r",stdin);
using namespace std;
int n,m,q;
int fa[maxn];
int _rank[maxn];
int first_max[maxn];
int second_max[maxn];
void init_set() {
for(int i=0; i<maxn; i++) {
fa[i] = i;
_rank[i] = 0;
first_max[i] = second_max[i] = 0;
}
}
int find_set(int x) {
if(x==fa[x]) return x;
int father = find_set(fa[x]);
_rank[x] += _rank[fa[x]];
return fa[x] = father;
}
void unit_set(int x, int y) {
int fa_x = find_set(x);
int fa_y = find_set(y);
int len1 = first_max[fa_x] + second_max[fa_x];
int len2 = first_max[fa_y] + second_max[fa_x];
if(len1 < len2) swap(x,y),swap(fa_x,fa_y);
fa[fa_y] = fa_x;
_rank[fa_y] = 1;
if(first_max[fa_y]+1 >= first_max[fa_x]) {
second_max[fa_x] = first_max[fa_x];
first_max[fa_x] = first_max[fa_y]+1;
} else if(first_max[fa_y]+1 >= second_max[fa_x]){
second_max[fa_x] = first_max[fa_y]+1;
}
}
int main(int argc, char const *argv[])
{
//IN;
while(scanf("%d %d %d", &n,&m,&q) != EOF)
{
init_set();
while(m--) {
int x,y; scanf("%d %d", &x, &y);
if(find_set(x) == find_set(y)) continue;
else unit_set(x, y);
}
while(q--) {
int type; scanf("%d", &type);
if(type == 1) {
int x; scanf("%d", &x);
int root = find_set(x);
int ans = first_max[root] + second_max[root];
printf("%d\n", ans);
} else {
int x,y; scanf("%d %d", &x, &y);
if(find_set(x) == find_set(y)) continue;
else unit_set(x, y);
}
}
}
return 0;
}
CodeForces 455C Civilization (并查集+树的直径)的更多相关文章
- Codeforces 455C Civilization(并查集+dfs)
题目链接:Codeforces 455C Civilization 题目大意:给定N.M和Q,N表示有N个城市,M条已经修好的路,修好的路是不能改变的.然后是Q次操作.操作分为两种.一种是查询城市x所 ...
- 51 nod 1427 文明 (并查集 + 树的直径)
1427 文明 题目来源: CodeForces 基准时间限制:1.5 秒 空间限制:131072 KB 分值: 160 难度:6级算法题 安德鲁在玩一个叫“文明”的游戏.大妈正在帮助他. 这个游 ...
- 【bzoj3362/3363/3364/3365】[Usaco2004 Feb]树上问题杂烩 并查集/树的直径/LCA/树的点分治
题目描述 农夫约翰有N(2≤N≤40000)个农场,标号1到N,M(2≤M≤40000)条的不同的垂直或水平的道路连结着农场,道路的长度不超过1000.这些农场的分布就像下面的地图一样, 图中农场用F ...
- BZOJ-3211花神游历各国 并查集+树状数组
一开始想写线段树区间开方,简单暴力下,但觉得变成复杂度稍高,懒惰了,编了个复杂度简单的 3211: 花神游历各国 Time Limit: 5 Sec Memory Limit: 128 MB Subm ...
- 洛谷P4092 [HEOI2016/TJOI2016]树 并查集/树链剖分+线段树
正解:并查集/树链剖分+线段树 解题报告: 传送门 感觉并查集的那个方法挺妙的,,,刚好又要复习下树剖了,所以就写个题解好了QwQ 首先说下并查集的方法趴QwQ 首先离线,读入所有操作,然后dfs遍历 ...
- BZOJ3211 花神游历各国 并查集 树状数组
欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ3211 题意概括 有n个数形成一个序列. m次操作. 有两种,分别是: 1. 区间开根(取整) 2. ...
- hdu 6200 mustedge mustedge(并查集+树状数组 或者 LCT 缩点)
hdu 6200 mustedge mustedge(并查集+树状数组 或者 LCT 缩点) 题意: 给一张无向连通图,有两种操作 1 u v 加一条边(u,v) 2 u v 计算u到v路径上桥的个数 ...
- 【bzoj4869】[Shoi2017]相逢是问候 扩展欧拉定理+并查集+树状数组
题目描述 Informatik verbindet dich und mich. 信息将你我连结. B君希望以维护一个长度为n的数组,这个数组的下标为从1到n的正整数.一共有m个操作,可以分为两种:0 ...
- CodeForces 455C Civilization(并查集+树直径)
好久没有写过图论的东西了,居然双向边要开两倍空间都忘了,不过数组越界cf居然给我报MLE??这个题题意特别纠结,一开始一直不懂添加的边长是多长... 题意:给你一些点,然后给一些边,注意没有重边 环, ...
随机推荐
- jQuery--隐藏事件
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- git cheat sheet,git四张手册图
- MVVM设计模式《网摘》
MVVM模式能够帮你把你程序的业务与展现逻辑从用户界面干净地分离开.保持程序逻辑与界面分离能够帮助解决很多开发以及设计问题,能够使你的程序能更容易的测试,维护与升级.它也能很大程度的增加代码重用性,并 ...
- QSettings读写注册表、配置文件
简述 一般情况下,我们在开发软件过程中,都会缓存一些信息到本地,可以使用轻量级数据库sqlite,也可以操作注册表.读写配置文件. 关于QSettings的使用前面已经介绍过了,比较详细,见" ...
- UVa 12063 (DP) Zeros and Ones
题意: 找出长度为n.0和1个数相等.没有前导0且为k的倍数的二进制数的个数. 分析: 这道题要用动态规划来做. 设dp(zeros, ones, mod)为有zeros个0,ones个1,除以k的余 ...
- Vagrant工具
Vagrant 是一款用来构建虚拟开发环境的工具,非常适合 php/python/ruby/java 这类语言开发 web 应用,“代码在我机子上运行没有问题”这种说辞将成为历史. 我们可以通过 Va ...
- json化表单数据
/** * josn化表单数据 * @name baidu.form.json * @function * @grammar baidu.form.json(form[, replacer]) * @ ...
- 安装服务Memcached+Nginx+Php linux下安装
Memcached安装 1. 源码安装libevent(下载地址:http://monkey.org/~provos/libevent/) 2. 源码安装memcached(下载地 ...
- MySql表中key的区别
我们看到Key那一栏,可能会有4种值,即'啥也没有','PRI','UNI','MUL'1. 如果Key是空的, 那么该列值的可以重复, 表示该列没有索引, 或者是一个非唯一的复合索引的非前导列2. ...
- 【转】Linux Posix Timer使用
原文网址:http://blog.csdn.net/hongszh/article/details/8608781 最强大的定时器接口来自POSIX时钟系列,其创建.初始化以及删除一个定时器的行动被分 ...