CF1338B Edge Weight Assignment 题解
解题思路
一种不需要树形 dp 的做法。
因为是一颗无根树,所以我们不妨以重心为根。
首先考虑边权最少的情况。可以发现这个时候对于任意两个叶子节点,我们可以分别考虑其到根节点的路径,这样对于求其路径的异或值是没有影响的,但是在这种情况下我们可以很方便的讨论其路径的奇偶性。令
为奇, 为奇,那么可以构造两条相同的路径使得其中边权均为相同的数,此时需要 种异或值; 为偶, 为奇,那么这个时候可以构造其中 到 的路径上有两种边权,不妨设它们为 和 ,且其中一种边权出现一次,剩下的均为另一种边权,同时,我们把 到 的路径上的边全部赋为 ,这种情况下,我们最少使用 种权值; 为奇, 为偶,同上; 为偶, 为偶,同样可以将 到 的路径上的边和 到 上的边全部赋为同一种权值,这时使用 种权值。
接下来考虑边权最多的情况。不难发现,拥有同一个父亲节点的叶子节点,它们到父亲节点的边权一定是相同的,那么我们可以考虑合并这些叶子节点。合并后观察可以得出,因为边权可以为任意正整数,那么一定存在一种构造方案使得任意两个叶子节点之间路径的异或值为
综上所述,本题解题步骤如下:
- 计算出重心,当然也可以选择其他不是叶子节点的点作为根节点;
- 计算所有叶子节点到根节点的距离,此时的距离等于两点之间的深度差的绝对值;
- 判断所有距离的奇偶性,若所有距离的奇偶性相同,那么最小值为
,否则为 ; - 因为合并后一个节点最多连向一个叶子节点,那么我们剩余的边数即为
,在计算距离的时候顺便判断计算一下即可。
AC 代码
#include<stdio.h>
#include<vector>
#include<algorithm>
#define N 100005
#define inf 2e9+7
int n;
std::vector<int> edge[N];
int root=1,sum;
int maxt[N],size[N];
inline void FindRoot(int u,int fa){
size[u]=1;
for(auto v:edge[u]){
if(v==fa) continue;
FindRoot(v,u);
size[u]+=size[v];
if(size[v]>maxt[u])
maxt[u]=size[v];
}if(sum-size[u]>maxt[u])
maxt[u]=sum-size[u];
if(maxt[u]<maxt[root])
root=u;
}
int depth[N],lea[N],cnt,tot[N];
inline void dfs(int u,int fa){
bool isLeavf=true;
for(auto v:edge[u]){
if(v==fa) continue;
depth[v]=depth[u]+1;
dfs(v,u);isLeavf=false;
}if(isLeavf){
lea[++cnt]=u;
++tot[fa];
}
}
signed main(){
scanf("%d",&n);int u,v;
for(register int i=1;i<n;++i){
scanf("%d%d",&u,&v);
edge[u].push_back(v);
edge[v].push_back(u);
}sum=n;maxt[root]=inf;
FindRoot(1,0);dfs(root,0);
int odd=0,eve=0,maxt=0,sema=0;
for(register int i=1;i<=cnt;++i){
if(depth[lea[i]]&1)
++odd;
else ++eve;
}if(!odd||!eve)
putchar('1');
else putchar('3');
putchar(' ');int sub=0;
for(register int i=1;i<=n;++i)
sub+=tot[i]?tot[i]-1:0;
printf("%d",n-1-sub);
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示