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 (并查集+树的直径)的更多相关文章

  1. Codeforces 455C Civilization(并查集+dfs)

    题目链接:Codeforces 455C Civilization 题目大意:给定N.M和Q,N表示有N个城市,M条已经修好的路,修好的路是不能改变的.然后是Q次操作.操作分为两种.一种是查询城市x所 ...

  2. 51 nod 1427 文明 (并查集 + 树的直径)

    1427 文明 题目来源: CodeForces 基准时间限制:1.5 秒 空间限制:131072 KB 分值: 160 难度:6级算法题   安德鲁在玩一个叫“文明”的游戏.大妈正在帮助他. 这个游 ...

  3. 【bzoj3362/3363/3364/3365】[Usaco2004 Feb]树上问题杂烩 并查集/树的直径/LCA/树的点分治

    题目描述 农夫约翰有N(2≤N≤40000)个农场,标号1到N,M(2≤M≤40000)条的不同的垂直或水平的道路连结着农场,道路的长度不超过1000.这些农场的分布就像下面的地图一样, 图中农场用F ...

  4. BZOJ-3211花神游历各国 并查集+树状数组

    一开始想写线段树区间开方,简单暴力下,但觉得变成复杂度稍高,懒惰了,编了个复杂度简单的 3211: 花神游历各国 Time Limit: 5 Sec Memory Limit: 128 MB Subm ...

  5. 洛谷P4092 [HEOI2016/TJOI2016]树 并查集/树链剖分+线段树

    正解:并查集/树链剖分+线段树 解题报告: 传送门 感觉并查集的那个方法挺妙的,,,刚好又要复习下树剖了,所以就写个题解好了QwQ 首先说下并查集的方法趴QwQ 首先离线,读入所有操作,然后dfs遍历 ...

  6. BZOJ3211 花神游历各国 并查集 树状数组

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ3211 题意概括 有n个数形成一个序列. m次操作. 有两种,分别是: 1. 区间开根(取整) 2. ...

  7. hdu 6200 mustedge mustedge(并查集+树状数组 或者 LCT 缩点)

    hdu 6200 mustedge mustedge(并查集+树状数组 或者 LCT 缩点) 题意: 给一张无向连通图,有两种操作 1 u v 加一条边(u,v) 2 u v 计算u到v路径上桥的个数 ...

  8. 【bzoj4869】[Shoi2017]相逢是问候 扩展欧拉定理+并查集+树状数组

    题目描述 Informatik verbindet dich und mich. 信息将你我连结. B君希望以维护一个长度为n的数组,这个数组的下标为从1到n的正整数.一共有m个操作,可以分为两种:0 ...

  9. CodeForces 455C Civilization(并查集+树直径)

    好久没有写过图论的东西了,居然双向边要开两倍空间都忘了,不过数组越界cf居然给我报MLE??这个题题意特别纠结,一开始一直不懂添加的边长是多长... 题意:给你一些点,然后给一些边,注意没有重边 环, ...

随机推荐

  1. [HDOJ4635]Strongly connected(强连通分量,缩点)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4635 题意:给一张图,问最多往这张图上加多少条边,使这张图仍然无法成为一个强连通图. 起初是先分析样例 ...

  2. Android Studio修改包名和applicationId的方法

    背景: 如果新做的项目跟以前做的某一个项目十分相似,那么一个简单的方法就是把原来项目拷贝一份,然后修改代码,但是这样包名还是原来项目的包名,还有如果想在同一台手机上同时安装新做的app和原来的app会 ...

  3. hdu 4617 Weapon(叉积)

    大一学弟表示刚学过高数,轻松无压力. 我等学长情何以堪= = 求空间无限延伸的两个圆柱体是否相交,其实就是叉积搞一搞 详细点就是求两圆心的向量在两直线(圆心所在的直线)叉积上的投影 代码略挫,看他的吧 ...

  4. 06day1

    Rabbit Number 枚举 [问题描述] 设 S(N)表示 N 的各位数字之和,如 S(484)=4+8+4=16,S(22)=2+2=4.如果一个正整数 x满足 S(x*x)=S(x)*S(x ...

  5. 【JS】js获得下拉列表选中项的值和id

    function tijiao(){ var elem = document.getElementById("dish_sort"); var index=elem.selecte ...

  6. 【转】有趣的Autolayout示例-Masonry实现

    原文网址:http://tutuge.me/2015/05/23/autolayout-example-with-masonry/ 好久没有写Blog了,这段时间有点忙啊=.=本文举了3个比较有“特点 ...

  7. Spring4整合Hibernate4

    首先,要明确Spring整合Hibernate可以做什么? 答案是: 1.由IOC容器来管理Hibernate的SessionFactory 2.让Hibernate使用上Spring的声明式事务 整 ...

  8. Oracle 课程四之索引

    课程目标 完成本课程的学习后,您应该能够: 理解b*tree索引的结构与特征 了解聚簇因子的产生原因 理解分区索引与全局索引的区别及场景 掌握组合索引的高效设计 位图索引的适用场景 全文索引的适用场景 ...

  9. 2014年acm亚洲区域赛·鞍山站

    今天北京赛站的比赛也结束了···看了一天的直播之后意识到鞍山站的比赛都过去了一个多月了···这一个月比较萎靡···整天都在睡觉写报告画工图中度过··· 鞍山比哈尔滨还是暖和很多的···就是山上有奇怪的 ...

  10. Java中的10颗语法糖

    语法糖(Syntactic Sugar):也称糖衣语法,指在计算机语言中添加的某种语法,这种语法对语言的功能没有影响,但是更方便程序员使用.通常来说,使用语法糖能够增加程序的可读性,减少程序代码出错的 ...