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??这个题题意特别纠结,一开始一直不懂添加的边长是多长... 题意:给你一些点,然后给一些边,注意没有重边 环, ...
随机推荐
- .net 程序员成长路线图?
https://www.zhihu.com/question/25474641 得看赵四本, @赵劼 推荐的. CLR via C# .net Essentials C# in Depth Frame ...
- Oracle数据泵导入导出数据,建立表空
Oracle11g 数据导入到oracle10g 中:1.在oracle11g 服务器命令行中用expdp 导出数据expdp ts/ts@orcl directory=expdp_dir dumpf ...
- 1287. Mars Canals(DP)
1287 水DP #include <iostream> #include<cstdio> #include<cstring> #include<algori ...
- 第六届华为创新杯编程大赛-进阶1第1轮 洞穴逃生 (bfs + 优先队列)
这个题容易出错想了挺长时间,然后代码不长,1Y.. 做完题,看了一下别人的博客,也可以优先用 闪烁法术, 在闪烁法术不不如跑步的阶段(即魔法恢复的时候)用跑步. 洞穴逃生 描述: 精灵王子爱好冒险,在 ...
- asp.net获取文件夹下的所有文件
using System; using System.Collections.Generic; using System.Web; using System.Web.UI; using System. ...
- php和java的一些比较
现在市场上的电子商务软件基本上可归结为两大阵营,即PHP阵营和Java阵营.但对接触电子商务不久的用户来说,看到的往往只是它们的表相,只是明显的价格差异,却很难看出它们之间的实际差异.其实,PHP+ ...
- [Sciter系列] MFC下的Sciter–3.Sciter脚本与底层交互
[Sciter系列] MFC下的Sciter–3.Sciter脚本与底层交互,脚本调用底层自定义的方法函数. 本系列文章的目的就是一步步构建出一个功能可用,接口基本完善的基于MFC框架的SciterF ...
- javascript实现继承的一种方式
function extend(Child, Parent) { var F = function(){}; F.prototype = Parent.prototype; Child.prototy ...
- 【C#学习笔记】读access2007
using System; using System.Data.OleDb; namespace ConsoleApplication { class Program { static void Ma ...
- c & c++中const
1 const的基本用法 常变量: const 类型说明符 变量名 #在c语言中,仍是一个变量.c++中则变为一个常量,比如可以设置为数组的长度 常引用: const 类型说明符 &引用名 ...