【XSY2505】tree

Description

机房断网了!xj轻而易举地撬开了中心机房的锁,拉着zwl走了进去。他们发现中心主机爆炸了。

中心主机爆炸后分裂成了 n 块碎片,但碎片仍然互相连接,形成一个树的结构。每个碎片有一个状态值0或1 。zwl找了一下规律,发现只有所有碎片的状态值相同的时候,主机才能够修复。

xj碰了碰其中一个碎片 x ,发现对于满足 x 到 v 的路径上所有碎片的状态值与 x 的状态值相同 的那些碎片 v 状态值都取反(0变1,1变0)了!

现在他们要尝试修复这个网络,最少需要多少次触碰呢?
Input
碎片从 1 到 n 编号。

第一行一个整数 n ,第二行 n 个数 0 或 1, 第 i 个数表示 i 号碎片的状态值。

接下来 n−1 行,每行两个数 x,y 表示 x 与 y 碎片中有连接。

Output

一行一个数,表示最少需要的碰撞次数。

Sample Input
11
0 0 0 1 1 0 1 0 0 1 1
1 2
1 3
2 4
2 5
5 6
5 7
3 8
3 9
3 10
9 11
Sample Output
2

HINT

样例解释:首先触碰三号碎片,再触碰六号碎片,这样所有碎片的状态值都会变为1 ,共触碰两次。

首先,他触碰的作用就是把一个以颜色相同为标准的连通块的颜色集体反转(下面的联通块的定义也是颜色相同)。

那我们可以将一个联通块缩成一个点。

正确答案就是缩点后的树的直径的一半(向上取整),\(d\)=树的直径,\(ans=[\frac{d+1}{2}]\)

为什么是这一个数?

一棵黑白相间的树,想要把整棵树变成相同的颜色,最少也要\([\frac{d+1}{2}]\)次。

接下来,为什么这\([\frac{d+1}{2}]\)次一定可以被取到呢?

在这棵树的直径上,必然有一个点是满足距离其他所有点的距离不超过\([\frac{d+1}{2}]\),否则与树的直径为\(d\)矛盾,\([\frac{d+1}{2}]+[\frac{d+1}{2}]+1>d\)

#include<bits/stdc++.h>
#define N 1000010
using namespace std;
struct data
{
    int s,n;
}bian[N];
int to[N],nxt[N],head[N],col[N],w[N],n,x,y,cnt,tot,nct,deep[N],maxn,nt,ntt;
void adde(int x,int y)
{
    to[++cnt]=y;
    nxt[cnt]=head[x];
    head[x]=cnt;
}
void dfs(int u,int fa)//缩点
{
    if(w[u]==w[fa]&&fa!=-1)
    {
        col[u]=col[fa];
    }else{
        col[u]=++tot;
        bian[++nct].s=col[fa];
        bian[nct].n=col[u];
    }
    for(int i=head[u];i;i=nxt[i])
    {
        int v=to[i];
        if(v!=fa)
        {
            dfs(v,u);
        }
    }
}
void slove(int u,int fa)//求直径
{
    for(int i=head[u];i;i=nxt[i])
    {
        int v=to[i];
        if(v==fa)
        {
            continue;
        }
        deep[v]=deep[u]+1;
        slove(v,u);
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&w[i]);
    }
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        adde(x,y);
        adde(y,x);
    }
    dfs(1,-1);
    memset(head,0,sizeof(head));
    cnt=0;
    for(int i=1;i<=nct;i++)
    {
        adde(bian[i].s,bian[i].n);
        adde(bian[i].n,bian[i].s);
    }
    slove(1,-1);//开始求树的直径
    maxn=-0x7f7f7f;
    for(int i=1;i<=tot;i++)
    {
        if(deep[i]>maxn)
        {
            nt=i;
            maxn=deep[i];
        }
    }
    memset(deep,0,sizeof(deep));
    slove(nt,-1);
    maxn=-0x7f7f7f;
    for(int i=1;i<=tot;i++)
    {
        if(deep[i]>maxn)
        {
            ntt=i;
            maxn=deep[i];
        }
    }
    printf("%d\n",(deep[ntt]+1)/2);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/2017gdgzoi44/p/11491152.html