zoukankan      html  css  js  c++  java
  • CodeForces 455C.Civilization(并查集)(树的直径)

    题意:给出一个由n个点,m条边组成的森林,两种类型共k次操作。
    类型一:给出点x,输出点x所在的树的直径的大小。
    类型二:给出点x,y(如果x,y在同一棵树中忽略此操作),在x所在树中选一个结点,在y所在的树中选择一个结点,连接这两个结点,要求使生成的树的直径最小。

    分析:首先给出公式,假设x所在的树的直径为del1,y所在的树的直径为del2,那么合并后的树的直径为(max(max(del1, del2), frac{(del1 + 1)}{2} + frac{(del2 + 1)}{2} + 1))。我们可以用并查集维护合并后的树,假设这棵合并后的树的代表元素为p,那么它的直径就为新的值。求取树的直径可以用dfs。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <vector>
    #include <algorithm>
    
    using namespace std;
    const int N = 300005;
    int h[N], e[N * 2], ne[N * 2], idx;
    bool st[N];
    void add(int a, int b)
    {
    	e[idx] = b, ne[idx] = h[a], h[a] = idx++;
    }
    
    int p[N];
    //每个集合的最长直径
    int deli[N];
    
    int find(int x)
    {
    	if (p[x] != x) p[x] = find(p[x]);
    	return p[x];
    }
    
    int res = 0;
    int now;
    int dfs(int u, int fa)
    {
    	st[u] = true;
    	//合并到根节点
    	p[u] = now;
    	//以这个点为枢纽的最长路径
    	int dist = 0;
    	int d1 = 0, d2 = 0;
    	for (int i = h[u]; i != -1; i = ne[i])
    	{
    		int j = e[i];
    		if (j == fa) continue;
    		int d = dfs(j, u) + 1;
    		dist = max(dist, d);
    		if (d >= d1) d2 = d1, d1 = d;
    		else if (d > d2) d2 = d;
    	}
    	res = max(res, d1 + d2);
    	return dist;
    }
    
    int main()
    {
    	//点、边、操作次数
    	int n, m, k;
    	scanf("%d%d%d", &n, &m, &k);
    
    	for (int i = 1; i <= n; ++i) p[i] = i;
    
    	memset(h, -1, sizeof h);
    	int u, v;
    	for (int i = 1; i <= m; ++i)
    	{
    		scanf("%d%d", &u, &v);
    		add(u, v), add(v, u);
    	}
    
    	for (int i = 1; i <= n; ++i)
    	{
    		if (!st[i])
    		{
    			st[i] = true;
    			now = i;
    			dfs(i, -1);
    			deli[i] = res;
    			res = 0;
    		}
    	}
    
    	int op, x, y;
    	for (int i = 1; i <= k; ++i)
    	{		
    		scanf("%d", &op);
    		if (op == 1)
    		{
    			scanf("%d", &x);
    			int pa = find(x);
    			printf("%d
    ", deli[pa]);
    		}
    		else
    		{
    			scanf("%d%d", &x, &y);
    			int pa = find(x), pb = find(y);
    			if (pa != pb)
    			{
    				int de1 = deli[pa], de2 = deli[pb];
    				int ndel = max(max(de1, de2), (de1 + 1) / 2 + (de2 + 1) / 2 + 1);
    				p[pa] = pb;
    				deli[pb] = ndel;
    			}
    		}
    	}
    
    	return 0;
    }
    
  • 相关阅读:
    MIPI CSI2学习(一):说一说MIPI CSI2
    图解数据结构树之AVL树
    查找树ADT--二叉查找树
    hisi mmz模块驱动讲解
    抓包工具 tcpdump 用法说明
    4. 海思Hi3519A MPP从入门到精通(四 视频输出)
    PHP无限分类分类导航LINK的代码实现
    PHP 无限极分类下拉列表实现
    PHP无限极分类原理
    微信支付回调验证签名处理
  • 原文地址:https://www.cnblogs.com/pixel-Teee/p/13284332.html
Copyright © 2011-2022 走看看