• CF455C Civilization


    题意翻译

    给出一个由 n 个点,m 条边组成的森林,有 q 组询问

    给出点 x,输出点 x所在的树的直径
    给出点 x,y,(如果 x,y在同一棵树中则忽略此操作)选择任意两点 u,v,使得 u 跟 x 在同一棵树中且 v 跟 y 在同一棵树中。将 u,v 之间连一条边,使得连边后的到的新树的直径最小
    输入格式
    第一行三个整数 n,m,q,分别表示 点的个数,边的个数和询问个数
    接下来 m 行,每行两个整数 x,y,表示有一条链接点 x,y 的边
    接下来 q 行,每行表示一条操作
    操作1:1 x
    操作2:2 x y

    输出格式

    输出行数为操作1的个数
    每行一个整数表示对应的操作一的答案

    说明与提示

    (n , m , q <= 3 imes 10 ^ 5)
    感谢 @_Wolverine 提供的翻译

    输入输出样例

    输入

    6 0 6
    2 1 2
    2 3 4
    2 5 6
    2 3 2
    2 5 3
    1 1

    输出

    4

    贪心+并查集。
    若有两个连通块x,y它们的直径分别是(c[x] , c[y]) 那么这两个连通块合并之后的直径是(max(c[x] , c[y] , frac{c[x]+1}{2} + frac{c[y]+1}{2} +1))
    也就是说会选取两个连通块的中点。
    正确性,假设不选直径的中点,比如x的连通块选了t这个点那么到直径的距离+直径的一段才是要加的东西,仔细想想这个比直径的一半要大。。(意会。。)
    剩下的就简单了,用并查集维护即可。

    写的时候没有注意联通了就不用连边了,导致c数组和自己又算了一遍,光荣的WA了。

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<bitset>
    #include<cstdio>
    #include<vector>
    #include<cmath>
    #include<queue>
    #include<set>
    #include<map>
    using namespace std;
    typedef long long LL;
    using namespace std;
    const int N = 3e5+10;
    inline int read()
    {
        register int x = 0 , f = 0; register char c = getchar();
        while(c < '0' || c > '9') f |= c == '-' , c = getchar();
        while(c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0' , c = getchar();
        return f ? -x : x;
    }
    int n , m , q , cnt , ans;
    int head[N] , vis[N] , c[N] , fa[N] , f[N];
    struct edge{ int v , nex; }e[N << 1];
    inline void add(int u , int v) { e[++cnt].v = v; e[cnt].nex = head[u]; head[u] = cnt; return ; }
    int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); }
    
    void dfs(int x)
    {
    	vis[x] = 1;
    	for(int i = head[x] , v; i ; i = e[i].nex)
    	{
    		v = e[i].v; if(vis[v]) continue; dfs(v);
    		ans = max(ans , f[x] + f[v] + 1); f[x] = max(f[x] , f[v] + 1);
    	}
    	return ;
    }
    
    int main()
    {
    	n = read(); m = read(); q = read();
    	for(int i = 1 ; i <= n ; ++i) fa[i] = i;
    	for(int i = 1 , u , v; i <= m ; ++i) u = read() , v = read() , add(u , v) , add(v , u) , fa[find(u)] = find(v);
    	for(int i = 1 ; i <= n ; ++i) if(!vis[i]) ans = 0 , dfs(i) , c[find(i)] = ans;
    //	for(int i = 1 ; i <= n ; ++i) cout << c[i] << ' '; cout << '
    ';
    	int op , x , y;
    	while(q--)
    	{
    		op = read(); x = read();
    		if(op == 1) cout << c[find(x)] << '
    ';
    		else
    		{
    			y = read() , x = find(x) , y = find(y); if(x == y) continue; // @@@
    			fa[x] = y , c[y] = max((c[x] + 1) / 2 + (c[y] + 1) / 2 + 1 , max(c[x] , c[y]));
    		}
    	}
    	return 0;
    }
    /*
    6 5 2
    1 2
    2 3
    3 4
    3 5
    5 6
    */
    
  • 相关阅读:
    SQL优化——union与union all 、left join 和 inner join 及 内连接
    用jdk自带工具jvisualvm查看系统内存使用情况
    不重启也能清理catalina.out日志的方法
    自己用crontab弄个定时任务备份日志重启项目
    三层架构的一点理解以及Dapper一对多查询
    Nuget包添加文件
    WPF启动流程-自己手写Main函数
    WPF实现手势解锁
    VS提交码云权限问题
    Json转Dictionary
  • 原文地址:https://www.cnblogs.com/R-Q-R-Q/p/12768239.html
Copyright © 2020-2023  润新知