51 nod 1427 文明 (并查集 + 树的直径)
Posted Przz
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了51 nod 1427 文明 (并查集 + 树的直径)相关的知识,希望对你有一定的参考价值。
1427 文明
题目来源: CodeForces
基准时间限制:1.5 秒 空间限制:131072 KB 分值: 160 难度:6级算法题
安德鲁在玩一个叫“文明”的游戏。大妈正在帮助他。
这个游戏里面有n个城市和m条双向的道路。城市从1到n编号。对于每一对城市,他们之间要么有唯一的一条道路,要么就是不可互达。一条道路的定义是一个包含不同城市的序列 v1, v2,...,vk , vi 和 vi+1 (1≤ i < k)之间有直接的一条道路相连。这条道路的长度是k-1。两个城市在同一区域的条件是当且仅当他们之间可以互达。
在游戏过程中,会有两个事件。
1. 安德鲁问大妈,城市x所在的区域里面最长的道路是多长。
2. 安德鲁要求大妈,合并城市x和城市y所在的区域。如果这两个城市本来在同一个区域,那么就不用合并。否则合并两个区域:从城市x所在区域选一个城市,再从城市y所在区域选一个城市,然后给这两个城市连一条边。选择这两个城市的时候要使得合并之后的区域里面最长道路尽可能小。如果有多种方案,任选一种即可。
大妈发现这些问题好难哦,所以他想请你来帮助一下。
Input
单组测试数据。
第一行包含三个整数n,m,q(1 ≤ n ≤ 3*10^5; 0 ≤ m < n; 1 ≤ q ≤ 3*10^5),分别表示城市数目,道路数目,查询数目。
接下来m行,每行有两个整数 ai 和bi (ai ≠ bi; 1 ≤ ai, bi ≤ n)。表示ai和bi之间有一条直接相连的道路。两个城市之间最多有一条直接相连的道路。
接下来q行的格式是下列之一:
1 xi 。(1 ≤ xi ≤ n)。这种情况要查询当前xi所在的区域的最长道路。
2 xi yi。 (1 ≤ xi, yi ≤ n)。这种情况下要求合并xi和yi所在的区域。注意:xi和yi可能是一样的。
Output
对于第1类查询,输出当前区域的最长路径长度。
Input示例
6 0 7
2 1 2
2 3 4
2 5 6
2 3 2
2 5 3
1 1
1 1
Output示例
4
4
/* 51 nod 1427 文明 (并查集 + 树的直径) problem: 给你n个城市,m条双向边连接,然后是q个查询 1 x: 城市x所在的区域的最长道路 2 x y:将两个城市的所在区域连接,要求维护最长路最短. solve: 一个区域的最长路就相当于树的直径.因为两个城市之间最多只有一条路径. 合并的话,如果要求最短,那么相当于将两条直径的中点相连. 所以已知A,B的直径可计算出新的直径的值 (注意还要与A,B的直径进行下比较). 先处理出每个区域的树的直径.然后可给每个区域打上标记,用并查集合并维护一下区域的树的直径. hhh-2016/09/27-17:21:09 */ #pragma comment(linker,"/STACK:124000000,124000000") #include <algorithm> #include <iostream> #include <cstdlib> #include <cstdio> #include <cstring> #include <vector> #include <map> #include <math.h> #define lson i<<1 #define rson i<<1|1 #define ll long long #define clr(a,b) memset(a,b,sizeof(a)) #define key_val ch[ch[root][1]][0] using namespace std; const int maxn = 3e5 + 100; const int inf = 0x3f3f3f3f; const ll mod = 1000000007; const double eps = 1e-7; template<class T> void read(T&num) { char CH; bool F=false; for(CH=getchar(); CH<‘0‘||CH>‘9‘; F= CH==‘-‘,CH=getchar()); for(num=0; CH>=‘0‘&&CH<=‘9‘; num=num*10+CH-‘0‘,CH=getchar()); F && (num=-num); } int stk[70], tp; template<class T> inline void print(T p) { if(!p) { puts("0"); return; } while(p) stk[++ tp] = p%10, p/=10; while(tp) putchar(stk[tp--] + ‘0‘); putchar(‘\n‘); } struct node { int v,next; } edge[maxn << 1]; ll Max; int tot,pos; int head[maxn]; int vis[maxn]; //int ans[i]; int pre[maxn]; struct Node { ll dis; int pre; } pnode[maxn]; void init(int n) { memset(head,-1,sizeof(head)); tot = 0; for(int i = 1; i <= n; i++) { vis[i] = 0; pnode[i].pre = i; pnode[i].dis = 1; } } void add_edge(int u,int v) { edge[tot].v = v,edge[tot].next = head[u],head[u] = tot ++; } void dfs(int u,int fa,ll len,int anc) { vis[u] = 1; pnode[u].pre = anc; if(len > Max) { Max = len; pos = u; } for(int i = head[u]; ~i; i = edge[i].next) { int v = edge[i].v; if(v == fa) continue; dfs(v,u,len + 1LL,anc); } } void cal(int now) { int from; pos = 0,Max =0 ; dfs(now,-1,0,now); from = pos; pos = 0,Max = 0; dfs(from,-1,0,now); pnode[now].dis = Max; } int fin(int u) { if(pnode[u].pre != u) return pnode[u].pre = fin(pnode[u].pre); return u; } void unio(int u,int v) { int ta = fin(u); int tb = fin(v); if(ta == tb) return ; if(ta > tb) swap(ta,tb); pnode[ta].pre =tb; ll tMax = 0; tMax = max(pnode[ta].dis,pnode[tb].dis); tMax = max(tMax,(pnode[ta].dis+1LL)/2LL+(pnode[tb].dis + 1LL)/2LL + 1LL); pnode[tb].dis = tMax; pnode[ta].dis= tMax; } int main() { int n,m,q; int u,v,ob; read(n),read(m),read(q); init(n); for(int i = 1; i <= m; i++) { read(u),read(v); add_edge(u,v); add_edge(v,u); } for(int i = 1;i <= n;i++) { if(!vis[i]) cal(i); } for(int i = 1; i <= q; i++) { read(ob); if(ob == 1) { read(u); int ta = fin(u); print(pnode[ta].dis); } else { read(u),read(v); unio(u,v); } } return 0; }
以上是关于51 nod 1427 文明 (并查集 + 树的直径)的主要内容,如果未能解决你的问题,请参考以下文章
51nod 1515 明辨是非 并查集+set维护相等与不等关系