• [CF455C] Civilization


    前言

    看似很水的却有坑的题

    题目

    洛谷

    CF

    讲解

    很明显,对于森林中的每棵树,我们都要先分别求出直径

    而合并可以用并查集维护

    关键在于如何合并

    不难想到是将两棵树直径的中点连边,但是新树的直径就是两棵树的直径分别除以二(向上取整)的和再加一吗?

    不是!

    比如两棵树的直径分别是10,0

    按上面那个方法算出来答案为6

    而答案明显是10

    也就是说我们需要对两棵树的直径取最大值

    再讲一个自己犯的错:直径指树边而不是树点

    代码

    int f[MAXN];
    int findSet(int x)
    {
    	if(x != f[x]) f[x] = findSet(f[x]);
    	return f[x];
    }
    
    bool vis[MAXN];
    int head[MAXN],tot,dp[MAXN],d;
    struct edge
    {
    	int v,nxt;
    }e[MAXN << 1];
    
    void Add_Edge(int x,int y)
    {
    	e[++tot].v = y;
    	e[tot].nxt = head[x];
    	head[x] = tot;
    }
    void Add_Double_Edge(int x,int y)
    {
    	Add_Edge(x,y);
    	Add_Edge(y,x);
    }
    void dfs(int x)
    {
    	vis[x] = 1;
    	for(int i = head[x]; i ;i = e[i].nxt)
    		if(!vis[e[i].v])
    		{
    			f[findSet(e[i].v)] = findSet(x);
    			dfs(e[i].v);
    			d = Max(d,dp[x] + dp[e[i].v] + 1);
    			dp[x] = Max(dp[x],dp[e[i].v]+1);
    		}
    }
    
    
    int main()
    {
    //	freopen(".in","r",stdin);
    //	freopen(".out","w",stdout);
    	n = Read(); m = Read(); Q = Read();
    	while(m--) Add_Double_Edge(Read(),Read());
    	for(int i = 1;i <= n;++ i) f[i] = i;
    	for(int i = 1;i <= n;++ i)
    		if(!vis[i]) d = 0,dfs(i),dp[i] = d;
    	while(Q--)
    	{
    		int opt = Read();
    		if(opt == 1) Put(dp[findSet(Read())],'
    ');
    		else 
    		{
    			int x = Read(),y = Read();
    			int u = findSet(x),v = findSet(y);
    			if(u == v) continue;
    			if(u > v) swap(u,v);
    			f[v] = u;
    			int t = Max(dp[u],dp[v]);
    			dp[u] = (dp[u]+1 >> 1) + (dp[v]+1 >> 1) + 1;
    			dp[u] = Max(dp[u],t);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    PS初步学习经验心得记录
    css第四天--复合选择器和样式三大特性
    css第三天--盒子模型
    html第二天--表单控件和表格
    css第二天--字体属性和文本属性
    vue计算属性(computed)
    ant-input的使用
    js-split() 方法
    设计转到前端开发
    $set
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/13516039.html
Copyright © 2020-2023  润新知