【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\) ,共触碰两次。

数据范围如下:

对于 \(20\%\) 的数据,\(n≤10\)

对于 \(100\%\) 的数据,\(n≤5×10^5\)


思路

题意:触碰这个点即为将这个点的同色联通块黑白取反,求需要触碰多少次使整棵树的颜色相同

我们发现这个题中有同色联通块,于是我们考虑先将同色联通块缩点

我们可以发现,缩点之后所形成的一棵树的相邻节点的颜色一定相反(黑白染色),那么我们考虑每次触碰一个点,都会将它取反,就会使他的颜色和上下的节点的颜色相同,再次缩点后,会发现树的直径每次最多减\(2\),于是一直这样实现,最后答案的最小值就是\(\left\lfloor\frac{d+1}{2}\right\rfloor\)

现在我们证明一下能否取到\(\left\lfloor\frac{d+1}{2}\right\rfloor\)

我们考虑一个点,它与其他点的距离最大不超过\(\left\lfloor\frac{d+1}{2}\right\rfloor\)

这个点是肯定存在的,因为满足\(\left\lfloor\frac{d+1}{2}\right\rfloor\)+\(\left\lfloor\frac{d+1}{2}\right\rfloor\)\(+1>n\)

接下来,我们每次都触碰使直径减少\(2\)个点,最后只剩下\(1\)个或\(2\)个点,就一定可以取到\(\left\lfloor\frac{d+1}{2}\right\rfloor\)


现在我们来考虑下怎么求树的直径(如果会的大佬可以直接跳到代码部分)

我们首先随意定一个根节点\(x\),找到距离\(x\)最远的节点\(y\),再找到距离\(y\)最远的节点\(z\),此时,\(y\)\(z\)的距离就是树的直径

void work(int u,int fa)
{
    d[u]=d[fa]+1;
    if(d[u]>d[maxn])maxn=u;
    for(int i=head[u];i;i=nxt[i])
        if(to[i]!=fa)work(to[i],u);
}

代码

#include<bits/stdc++.h>
using namespace std;
const int N=5*1e5+10;
int n,cnt=0,tot=0;
int col[N];
int bok[N];
int to[N<<1];
int nxt[N<<1];
int head[N];
struct edge
{
    int u,v;
}e[N];
void add(int u,int v)
{
    to[++cnt]=v;
    nxt[cnt]=head[u];
    head[u]=cnt;
}
void dfs(int u,int fa)//缩点
{
    if(fa==0||col[u]!=col[fa])
    {
        bok[u]=++tot;
        if(fa)e[tot-1]=(edge){bok[u],bok[fa]};
    }
    else bok[u]=bok[fa];
    for(int i=head[u];i;i=nxt[i])
        if(to[i]!=fa)dfs(to[i],u);
}
int maxn=0;
int d[N];
void work(int u,int fa)
{
    d[u]=d[fa]+1;
    if(d[u]>d[maxn])maxn=u;
    for(int i=head[u];i;i=nxt[i])
        if(to[i]!=fa)work(to[i],u);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&col[i]);
    int a,b;
    for(int i=1;i<n;i++)
    {
        scanf("%d %d",&a,&b);
        add(a,b);add(b,a);
    }
    dfs(1,0);
    cnt=0;
    memset(head,0,sizeof(head));
    for(int i=1;i<tot;i++)add(e[i].u,e[i].v),add(e[i].v,e[i].u);
    //开始求树的直径
    work(1,0);//找离节点1最远的节点
    int pos=maxn;
    maxn=0;
    work(pos,0);//找离节点pos最远的节点
    //求树的直径结束,d[maxn]为树的直径
    printf("%d",d[maxn]/2);
    return 0;
}

转载于:https://www.cnblogs.com/ShuraEye/p/11505795.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值