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??这个题题意特别纠结,一开始一直不懂添加的边长是多长... 题意:给你一些点,然后给一些边,注意没有重边 环, ...
随机推荐
- ios绘图时的坐标处理
在iOS中,进行绘图操作时,一般主要是在UIView:drawRect中调用 UIGraphicsBeginImageContextWithOptions等一系列函数,有时候直接画图就行,比如UIIm ...
- java 菱形
//画菱形 一半 for(int hs=1;hs<11;hs++) //行数 { //画空格 for(int kg = 9; kg >= hs; kg--) //空格数 { System. ...
- [POJ1236]Network of Schools(并查集+floyd,伪强连通分量)
题目链接:http://poj.org/problem?id=1236 这题本来是个强连通分量板子题的,然而弱很久不写tarjan所以生疏了一下,又看这数据范围觉得缩点这个事情可以用点到点之间的距离来 ...
- AVL的旋转
转自http://blog.csdn.net/gabriel1026/article/details/6311339 平衡二叉树在进行插入操作的时候可能出现不平衡的情况,AVL树即是一种自平衡的二叉树 ...
- 函数buf_page_create
/********************************************************************//** Initializes a page to the ...
- hdu 4604 Deque(最长不下降子序列)
从后向前对已搜点做两遍LIS(最长不下降子序列),分别求出已搜点的最长递增.递减子序列长度.这样一直搜到第一个点,就得到了整个序列的最长递增.递减子序列的长度,即最长递减子序列在前,最长递增子序列在后 ...
- macro names must be identifiers
1.错把 #include 写成了 #define 会报这个错 2.定义一个不存在的宏业会报这个错,如加了-DANDRO 而ANDRO不存在
- andorid 下拉刷新
1:android的下拉刷新操作是需要一个ListView,通过onTouchEvent来判断用户的手势操作,用户触摸屏幕并且下拉时,当下拉超过指定的设定高度时就提示用户进行刷新. 2:当进行刷新时, ...
- css的框架——global.css
global.css,一般这个css文件是用于装全站主要框架css样式代码. “global”翻译为全局.全部.从翻译中大家也能理解global.css用于做什么.大站常常用于装全站公共的CSS样式选 ...
- CXF之jaxws:endpoint对spring bean的引用
由于CXF对spring的无缝支持,CXF的使用,经常与spring捆绑在一起.随之而起的,自然是想在jaxws:endpoint中引用spring bean.在CXF提供的HelloWorld例子中 ...