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

时间:2022-11-10 07:58:23

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&lpar;并查集&plus;dfs&rpar;

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

  2. 51 nod 1427 文明 &lpar;并查集 &plus; 树的直径&rpar;

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

  3. 【bzoj3362&sol;3363&sol;3364&sol;3365】&lbrack;Usaco2004 Feb&rsqb;树上问题杂烩 并查集&sol;树的直径&sol;LCA&sol;树的点分治

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

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

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

  5. 洛谷P4092 &lbrack;HEOI2016&sol;TJOI2016&rsqb;树 并查集&sol;树链剖分&plus;线段树

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

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

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

  7. hdu 6200 mustedge mustedge&lpar;并查集&plus;树状数组 或者 LCT 缩点&rpar;

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

  8. 【bzoj4869】&lbrack;Shoi2017&rsqb;相逢是问候 扩展欧拉定理&plus;并查集&plus;树状数组

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

  9. CodeForces 455C Civilization(并查集&plus;树直径)

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

随机推荐

  1. MySQL中函数CONCAT及GROUP&lowbar;CONCAT

    一.CONCAT()函数CONCAT()函数用于将多个字符串连接成一个字符串.使用数据表Info作为示例,其中SELECT id,name FROM info LIMIT 1;的返回结果为+----+ ...

  2. JS学习知我见(常用建站代码)

    <!doctype html><html><head><meta charset="utf-8"><meta name=&qu ...

  3. Android之SurfaceView学习(一)

    对应的中文翻译SurfaceView是视图(View)的继承类,这个视图里内嵌了一个专门用于绘制的Surface.你可以控制这个Surface的格式和尺寸.Surfaceview控制这个Surface ...

  4. HTML&plus;CSS笔记 CSS笔记集合

    HTML+CSS笔记 表格,超链接,图片,表单 涉及内容:表格,超链接,图片,表单 HTML+CSS笔记 CSS入门 涉及内容:简介,优势,语法说明,代码注释,CSS样式位置,不同样式优先级,选择器, ...

  5. 【2017-06-27】Js中获取地址栏参数、Js中字符串截取

    一.Js中获取地址栏参数 //从地址栏获取想要的参数 function GetQueryString(name) { var reg = new RegExp("(^|&)&quot ...

  6. PHPMailer &lt&semi; 5&period;2&period;21 - Local File Disclosure(CVE-2017-5223)

    CVE-2017-5223 :PHPMailer < 5.2.21 - Local File Disclosure 本文主要介绍一下PHPMailer < 5.2.21 - Local F ...

  7. Jetty - 教程

    Jetty使用教程(一)——开始使用Jetty : https://www.cnblogs.com/yiwangzhibujian/p/5832597.html jetty 的工作原理以及与 Tomc ...

  8. python自定义mininet拓扑

    python自定义mininet拓扑 前言 闲来无聊,想到很早之前都是用GUI来自定义拓扑,这次用python来自定义一下(以前留下的苦果) 转自Mininet 自定义网络拓扑 过程相对简单 实现过程 ...

  9. 深入理解PHP 数组之count 函数

    count()PHP count() 函数用于计算数组中的单元数目或对象中的属性个数,返回数组的单元个数或对象中的属性个数.语法:int count( mixed var [, int mode] ) ...

  10. 【MySQL】基本语句

    ##mac启动mysql的命令语句## /usr/local/mysql/bin/mysql -u root -p ##然后输入密码 查看当前存在数据库 show databases; 创建数据库 c ...