【UER #4】被删除的黑白树

6 篇文章 0 订阅
2 篇文章 0 订阅

前言

首先,星期五晚的网络赛我居然忘了报名。。。。
于是我看了看题,发现我无力回天了。。。。
(星期五晚的主持人大赛好看吗?表示和cty一起做题便基本没看八台大戏了)

题目

很久很久以前,有一棵树加入了 UOJ 群。

这天,在它讨论“一棵树应该怎么旋转”的时候一不小心被删除了,变成了被删除的树。

突然间,它突然发现它失去了颜色,变成了一棵纯白的树。这让它感觉很焦躁,于是它来拜托你给自己染上一些颜色。

我们可以把它描述为一棵 n 个节点的有根树(默认树的根为 1 号节点),所有非根的度数为 1 的节点被称为叶子节点。最开始所有的节点都是白色的。

现在你需要选出一些节点并把这些节点染成黑色的。为了迎合树的审美,你的染色方案必须要满足所有叶子节点到根路径上的黑色节点个数相同。

你发现黑色节点个数越多,树就会越高兴,所以你想要知道在所有合法的染色方案中,黑色节点总个数最多是多少。
n≤100000

第一感觉

好像叶子的颜色啊。。感觉是dfs之类的加贪心,但实际贪心的策略是比较优美的。。

60%

我们发现若根到每个叶子节点的染色数一样,那么每个子树也一定成立
所以我们可以用f[i,j]表示树的第i个点到它下面的叶子节点为j的最大染色数。 f[i,j]=max(k=son[i]f[k,j],k=son[i]f[k,j1]+1)
O(n2) ,可以得60分

分析

我们可以发现,每个叶子节点往上的染色数一定是离根节点最近的叶子节点的深度minh。
于是我们便可以很贪心的去染色,且保证可以在每个叶子节点往上有minh个染色的点。
接着我们发现还是很难解决这个问题,但我们可以转化:
正难则反!!!
我们可以染最少的白色点。
那么我们只要找到某些点必须染白的便是最优的,
即:

结论:若一棵树到其最浅的叶节点上经过白色节点,那么根到所有的叶节点的路径上必然经过白色节点。因为该路径上黑色节点数量为(树的最浅叶节点深度-该路径上白色节点数量),而对于其他叶节点,树的最浅叶节点深度≤该叶节点深度,又满其与最浅叶节点经过的黑点数量相等,则在这条路径上必然存在白色节点。

我们仅需dfs时通过判断若
当前点的子树最浅的叶子节点的深度-当前走到这个点染了x个白色点>从跟到最浅的叶子节点的深度
那么我们便必须把这个点染成白色。
这样便可以O(n)解决问题了。

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int N=200005;
int n,x,y,b[N],last[N],next[N],nu,d[N],low[N];
void insert(int x,int y){
     b[++nu]=y;next[nu]=last[x];last[x]=nu;
}
void dfs(int x,int y,int z){
     int p=last[x];
     if (low[x]-y>low[1]) d[x]=1;
     while (p!=0){
           if (b[p]!=z){
           if (d[x]) dfs(b[p],y+1,x);else dfs(b[p],y,x);}
           p=next[p];
     }
}
void dfe(int x,int y,int z){
     int p=last[x];
     low[x]=200000;
     bool pe=0;
     while (p!=0){ 
           if (b[p]!=z) {
           dfe(b[p],y+1,x);pe=1;
           low[x]=min(low[x],low[b[p]]);
           }
           p=next[p];
     }
     if (!pe) low[x]=y;
}
int main(){
    scanf("%d",&n);
    fo(i,1,n-1){
      scanf("%d%d",&x,&y);
      insert(x,y);
      insert(y,x);
    }
    dfe(1,0,0);
    dfs(1,0,0);int sum=0;
    fo(i,1,n)if (d[i]) sum++;
    printf("%d",n-sum);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值