codeforces 455C 并查集

传送门

给n个点, 初始有m条边, q个操作。

每个操作有两种, 1是询问点x所在的连通块内的最长路径, 就是树的直径。 2是将x, y所在的两个连通块连接起来,并且要合并之后的树的直径最小,如果属于同一个连通块就忽视这个操作。

先dfs出每个连通块的初始直径, 然后合并的话就是len[x] = max( (len[x]+1)/2+(len[y]+1)/2+1, max(len[x], len[y]));  然后搞一搞就好了。

一开始写bfs写挫了一直超时,  只好改成dfs......

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define pb(x) push_back(x)
  4 #define ll long long
  5 #define mk(x, y) make_pair(x, y)
  6 #define lson l, m, rt<<1
  7 #define mem(a) memset(a, 0, sizeof(a))
  8 #define rson m+1, r, rt<<1|1
  9 #define mem1(a) memset(a, -1, sizeof(a))
 10 #define mem2(a) memset(a, 0x3f, sizeof(a))
 11 #define rep(i, a, n) for(int i = a; i<n; i++)
 12 #define ull unsigned long long
 13 typedef pair<int, int> pll;
 14 const double PI = acos(-1.0);
 15 const double eps = 1e-8;
 16 const int mod = 1e9+7;
 17 const int inf = 1061109567;
 18 const int dir[][2] = { {-1, 0}, {1, 0}, {0, -1}, {0, 1} };
 19 const int maxn = 3e5+5;
 20 int f[maxn], num[maxn], head[maxn*2], dis[maxn], cnt, q[maxn*2];
 21 struct node
 22 {
 23     int to, nextt;
 24 }e[maxn*2];
 25 void add(int u, int v) {
 26     e[cnt].to = v;
 27     e[cnt].nextt = head[u];
 28     head[u] = cnt++;
 29 }
 30 int findd(int u) {
 31     return f[u] == u?u:f[u] = findd(f[u]);
 32 }
 33 void unionn(int x, int y) {
 34     x = findd(x);
 35     y = findd(y);
 36     if(x == y)
 37         return ;
 38     f[y] = x;
 39     int now = (1+num[x])/2+(num[y]+1)/2+1;
 40     num[x] = max(now, max(num[x], num[y]));
 41 }
 42 int maxx = 0, pos = -1;
 43 int dfs(int u, int fa, int d) {
 44     if(d>maxx) {
 45         maxx = d;
 46         pos = u;
 47     }
 48     for(int i = head[u]; ~i; i = e[i].nextt) {
 49         int v = e[i].to;
 50         if(v == fa)
 51             continue;
 52         dfs(v, u, d+1);
 53     }
 54 }
 55 template<typename __ll>
 56 inline void read(__ll &m)
 57 {
 58     __ll x=0,f=1;char ch=getchar();
 59     while(!(ch>=‘0‘&&ch<=‘9‘)){if(ch==‘-‘)f=-1;ch=getchar();}
 60     while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
 61     m=x*f;
 62 }
 63 int main()
 64 {
 65     int n, m, q, x, y;
 66     cin>>n>>m>>q;
 67     mem1(head);
 68     for(int i = 1; i<=n; i++) {
 69         f[i] = i;
 70         num[i] = 0;
 71     }
 72     while(m--) {
 73         read(x); read(y);
 74         add(x, y);
 75         add(y, x);
 76         x = findd(x); y = findd(y);
 77         f[x] = y;
 78     }
 79     for(int i = 1; i<=n; i++) {
 80         if(f[i] == i) {
 81             dfs(i, -1, 0);
 82             maxx = 0;
 83             if(pos == -1)
 84                 continue;
 85             dfs(pos, -1, 0);
 86             num[i] = maxx;
 87             maxx = 0;
 88             pos = -1;
 89         }
 90     }
 91     while(q--) {
 92         int sign;
 93         read(sign);
 94         if(sign == 1) {
 95             read(x);
 96             printf("%d\n", num[findd(x)]);
 97         } else {
 98             read(x); read(y);
 99             unionn(x, y);
100         }
101     }
102 }
时间: 2024-11-20 18:38:10

codeforces 455C 并查集的相关文章

codeforces 366D 并查集

//给你一个无向图,图的每条边有一个范围,所选数x要在这个范围能过这条边 //求x最大范围 //枚举所有的边的右边,对于所选右边找左边最小值,用并查集判断是否可行 #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> using namespace std ; const int maxn = 1010; int F[maxn] ; int find(int x)

0-1-Tree CodeForces - 1156D (并查集)

大意: 给定树, 边权为黑或白, 求所有有向路径条数, 满足每走过一条黑边后不会走白边. 这题比赛的时候想了个假算法, 还没发现..... 显然所求的路径要么全黑, 要么全白, 要么先全白后全黑, 所以可以用并查集将相邻同色边合并即可. #include <iostream> #include <random> #include <algorithm> #include <cstdio> #include <math.h> #include &l

Codeforces 722C(并查集 + 思维)

题目链接:http://codeforces.com/problemset/problem/722/C 思路: 题目给的操作数从第 1 个到第 N 个数是删除原数组中的一个数, 那么反过来从后往前就是增加原数组中的一个数, 每增加一个值, 那么就有四种情况: 第一种和前后都不连续, 即自成一个集合; 第二种:和前面的数连续, 即和前一个数在一个集合; 第三种和之后一个数连续, 即和之后的一个数在一个集合; 第四种既和前面一个数连续也和后面一个数连续,那么说明前后两个集合被这个数合并成一个集合.

coderforces 455C 并查集+树的直径

点击打开链接 题意:有两种操作,1是问有u的最长路径的长度,2是将u与v的两个集合合并,但是要使得合并后的集合的最长路径最小 思路:因为后面的操作才会改变路径长度,可以先将所有长度预处理出来,在一个集合的元素最长路径相同,然后再执行询问操作,就是这个预处理真的是醉了,就是两次BFS求出最大值,但是很有可能超时,因为如果两个点在一个集合,就要走150000次,然后再判断到每个点的最大值,这样肯定超时,我们将这个集合的元素记录下来判断这些元素就行,就不会超时了,然后就是并查集合并的部分了,两个集合A

CodeForces 566D 并查集集合合并

1 #include <stdio.h> 2 #include <algorithm> 3 #define MAX 100000 4 #define LL long long 5 #define unsigned U 6 //using namespace std; 7 int cas=1,T; 8 int fa[MAX*2+10],next[MAX*2+10],n,q; 9 void init(int n) 10 { 11 for(int i=1;i<=n;i++) { f

codeforces 455C C. Civilization(树形dp+树的直径+并查集)

题目链接: codeforces 455C 题目大意: 给出一些点,他们之间初始存在一些边,给出两种操作,第一种是查询某个点所在的树的直径,另一种是将两个树合并,要求使合并后的树的直径最小. 题目分析: 首先算取没做操作前的连通块里的树的直径,也就是先dfs一遍,找到深度最大的点,然后从这个点再搜,找到的最远的距离就是这棵树的直径,因为可以证明从根搜深度最大的点一定是树的直径的一个端点,因为它可以通过到达次大的深度的点或者找到与它公共祖先不在根处的获得树的直径. 然后每次合并,我们可以知道得到的

Codeforces 455C Civilization(并查集+dfs)

题目链接:Codeforces 455C Civilization 题目大意:给定N,M和Q,N表示有N个城市,M条已经修好的路,修好的路是不能改变的,然后是Q次操作,操作分为两种,一种是查询城市x所在的联通集合中,最长的路为多长.二是连接两个联通集合,采用联通之后最长路最短的方案. 解题思路:因为一开时的图是不可以改变的,所以一开始用dfs处理出各个联通集合,并且记录住最大值,然后就是Q次操作,用并查集维护,注意因为联通的时候要采用最长路径最短的方案,所以s的转移方程变为s = max(s,

Codeforces Round #396 (Div. 2) D题Mahmoud and a Dictionary(并查集)解题报告

Mahmoud wants to write a new dictionary that contains n words and relations between them. There are two types of relations: synonymy (i. e. the two words mean the same) and antonymy (i. e. the two words mean the opposite). From time to time he discov

Codeforces 755C:PolandBall and Forest(并查集)

http://codeforces.com/problemset/problem/755/C 题意:该图是类似于树,给出n个点,接下来p[i]表示在树上离 i 距离最远的 id 是p[i],如果距离相等则p[i]是 id 较小的点. 思路:一开始没什么想法,画几分钟图发现不到什么东西,后来想着 i 和 p[i] 有关系,那么就代表 i 和 p[i] 是属于同一棵树,那么不就是并查集了嘛.抱着试一试的心态搞了一下居然过了. 1 #include <cstdio> 2 #include <c