Codeforce Round #379 (Div. 2) 734 E Anton and Tree(tree 缩点 树的直径 )

题目链接

734 E Anton and Tree
题目大意:一棵树上有两种不同颜色的节点,你有一种操作,一次可以改变与 v 相连的同种颜色联通快的颜色,问最少多少次操作可以把树染成同样的颜色

分析

看了题解之后才明白了,因为一次染色可以改变同一个连通块,所以首先想到把同一个连通块缩成一个节点,这样是不影响染色操作的,这样我们就只需要考虑一颗相邻节点颜色不同的树需要多少次操作才能把颜色变为一样的了,
首先我们指出染色操作不可能少于(floor[(d+1)/2]),d是缩点之后的树的直径,想象最平凡的情况,树就是一条链,你一次染色最多让直径减少2,(在非树的端点节点)
下面我们证明最小染色次数就是 (floor[(d+1)/2]) ,我们可以用induction来证明, d=0 的时候是显然的,下面我们来推 d=n 的时候成立
首先,树的直径上的点的非直径分支的长度不可能长于他在直径上的最短分支的长度,那么我们可以在直径上找到一个非树上分支最长的节点(不选端点),对他染色,这样可以保证染色缩点后的树 T 的直径还经过原来的树 T ,因为此时仍能够保证这条直径上的点的非直径分支不超过直径上的分支。这样我们就归约到了d2的情况,所以最后答案
(floor[(d+1)/2])
题解原文,(我的证明和原文不一样)
所以这道题只需要缩点求直径,个人代码功底太弱写的很挫~

AC代码

#include <cstdio>
#include <cstring>
#include <queue>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<vector>
#define INF 0x3f3f3f3f
#define maxn 200009
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int,int> PI;

struct Edge{
    int u,v;
};
int ans;

int color[maxn];

int p[maxn];
bool vis[maxn];
int find(int x){return p[x] == x? x:p[x] = find(p[x]);}
void Union(int x,int y)
{
    x = find(x),y = find(y);
    if(x!=y)p[x] = y;
}
struct Graph{
    Edge edges[maxn*2];
    int n,ne;
    int ft[maxn];
    int nt[maxn*2];
    int d[maxn];
    void init()
    {
        ne = 0;
        memset(ft,-1,sizeof(ft));
        memset(nt,-1,sizeof(nt));
        memset(d,0,sizeof(d));
    }
    void add_edge(int u,int v)
    {
        edges[ne].u = u,edges[ne].v = v;
        nt[ne] = ft[u];
        ft[u] = ne;
        ne++;
    }
    void bfs(int s)
    {
        queue<int> Q;
        Q.push(s);
        d[s] = 0;
        while(!Q.empty())
        {
            int u = Q.front();Q.pop();
            for(int e = ft[u] ; e!=-1 ; e = nt[e])
            {
                int v = edges[e].v;
                if(d[v]==0 && v!=s){
                    d[v] = d[u]+1;
                    Q.push(v);
                }
            }
        }
    }
    int diamiter()
    {
        int s = find(1);
        bfs(s);
        s = 1;
        for(int i=1 ; i<n ; ++i)
            if(d[s]<d[i])s = i;
        memset(d,0,sizeof(d));
        bfs(s);
        int res = -1;
        for(int i=1 ; i<=n ; ++i)
            res = max(res,d[i]);
        return res;
    }

};
Graph G1,G2;
//并查集缩点
void compress(Graph & G)
{
    for(int i=0 ; i<G.ne ; ++i)
    {
        int u = G.edges[i].u,v = G.edges[i].v;
        if(color[u] == color[v])Union(u,v);
    }
}
//建新树
void dfs(int s,int p)
{
    vis[s] = true;
    for(int e = G1.ft[s] ; e!=-1 ; e = G1.nt[e])
    {
        int v = G1.edges[e].v;
        if(color[v]!=color[p])G2.add_edge(p,find(v));
        else if(!vis[v])dfs(v,p);
    }
}

int main()
{
        //freopen("H:\\c++\\file\\stdin.txt","r",stdin);
       int n;
       cin>>n;
       G1.init();G2.init();
       for(int i=1 ; i<=n ; ++i)p[i] = i;
        memset(vis,false,sizeof(vis));
       G1.n = G2.n = n;
       for(int i=1 ; i<=n ; ++i)cin>>color[i];
        for(int i=0 ; i<n-1 ; ++i)
        {
            int u,v;
           cin>>u>>v;
           G1.add_edge(u,v);
           G1.add_edge(v,u);
        }
       compress(G1);
       for(int i=1 ; i<=n ; ++i)
            if(p[i] == i)dfs(i,i);
       int ans = G2.diamiter();
       cout<<(ans+1)/2<<endl;
       return 0;
}






  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值