当前位置: 首页 > news >正文

赛前训练 12 树的直径、中心和重心

A

树的直径板子.

B

注意到树的直径有个性质:所有节点到其他点的最远距离一定在直径的端点处取到.怎么证明请查阅往期笔记.

这样,我们把直径留着,将其他点依次和端点匹配,最后加上直径的贡献就得到了第一问的答案.

那么如何输出方案?画个图可以发现取出其他点的过程类似于拓扑排序,于是我们模拟一遍拓扑排序即可.最后直径跳 fa 输出就好啦.

实现
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <stdlib.h>
#include <vector>
#include <queue>
#include <cmath>
#include <stack>
#include <map>
#include <set>
#define int long long
using namespace std;const int N=2e5+5;
int n,x,y;
int dis[N],dis2[N],dg[N],f[N];
bool vis[N];
struct NODE{int v,w;
};
vector<NODE> G[N];void dfs(int cur,int fa,int *d){f[cur]=fa;for(auto [i,w]:G[cur]){if(i==fa)continue;d[i]=d[cur]+1;dfs(i,cur,d);}
}
void mark(){int cur=x;while(cur!=y){vis[cur]=1;cur=f[cur];}vis[y]=1;
}signed main(){ios::sync_with_stdio(0);cin.tie(0);cin>>n;for(int i=1,u,v;i<n;i++){cin>>u>>v;G[u].push_back({v,0});G[v].push_back({u,0});dg[u]++,dg[v]++;}dfs(1,0,dis);for(int i=1;i<=n;i++)if(dis[x]<dis[i])x=i;memset(dis,0,sizeof dis);dfs(x,0,dis);for(int i=1;i<=n;i++)if(dis[y]<dis[i])y=i;dfs(y,0,dis2);int ans=dis[y]*(dis[y]+1)/2;mark();for(int i=1;i<=n;i++)if(!vis[i])ans+=max(dis[i],dis2[i]);cout<<ans<<'\n';queue<int> q;for(int i=1;i<=n;i++)if(dg[i]==1&&!vis[i])q.push(i);while(!q.empty()){int cur=q.front(),cur2=y;if(dis[cur]>dis2[cur])cur2=x;cout<<cur<<' '<<cur2<<' '<<cur<<'\n';q.pop();for(auto [i,w]:G[cur]){if(vis[i])continue;dg[i]--;if(dg[i]==1)q.push(i);}}int cur=x;while(cur!=y){cout<<cur<<' '<<y<<' '<<cur<<'\n';cur=f[cur];}return 0;
}

C

一个强力性质:两棵树连在一起后要求直径最大必须连中心,因为最长链都经过中心.于是这个题枚举断开的边然后暴力求中心连边最后求直径取 \(\max\) 即可.怎么都是性质题呜呜呜被薄纱了

实现
#include<bits/stdc++.h>
#define int long long
using namespace std;const int N=5e3+5;
int n,ans=1e18,f[N];
int dis,dis1,dis2;
struct E{ int v,w; };
vector<E> T[N<<1];
int len1[N],len2[N],up[N];
bool vis[N];
int u[N],v[N],w[N];inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}while(ch>='0' && ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;
}
void dfsd(int cur,int fa){vis[cur]=1;for(auto i:T[cur]){if(i.v==fa) continue;dfsd(i.v,cur);if(len1[i.v]+i.w>len1[cur])len2[cur]=len1[cur],len1[cur]=len1[i.v]+i.w;else if(len1[i.v]+i.w>len2[cur])len2[cur]=len1[i.v]+i.w;}dis=max(dis,len1[cur]+len2[cur]);
}
void dfsu(int cur,int fa){for(auto i:T[cur]){if(i.v==fa) continue;up[i.v]=up[cur]+i.w;if(len1[i.v]+i.w!=len1[cur])up[i.v]=max(up[i.v],len1[cur]+i.w);elseup[i.v]=max(up[i.v],len2[cur]+i.w);dfsu(i.v,cur);}
}
int fnd(int x){return (f[x]==x?x:f[x]=fnd(f[x]));
}
void uni(int x,int y){x=fnd(x),y=fnd(y);if(x!=y) f[x]=y;
}signed main(){//ios::sync_with_stdio(0);n=read();for(int i=1;i<n;i++)u[i]=read(),v[i]=read(),w[i]=read();for(int i=1;i<n;i++){for(int j=1;j<=n;j++) f[j]=j;for(int j=1;j<=n;j++) T[j].clear();for(int j=1;j<=n;j++) len1[j]=len2[j]=up[j]=0; dis1=dis2=0;for(int j=1;j<n;j++)if(j!=i) uni(u[j],v[j]),T[u[j]].push_back({v[j],w[j]}),T[v[j]].push_back({u[j],w[j]});dis=0,dfsd(u[i],0);dis1=dis;dis=0,dfsd(v[i],0);dis2=dis;dfsu(u[i],0),dfsu(v[i],0);int minx=1e18,miny=1e18;for(int j=1;j<=n;j++){if(fnd(j)==fnd(u[i])&&minx>max(len1[j],up[j]))minx=max(len1[j],up[j]);if(fnd(j)==fnd(v[i])&&miny>max(len1[j],up[j]))miny=max(len1[j],up[j]);}ans=min(ans,max({dis1,dis2,w[i]+minx+miny}));}cout<<ans;return 0;
}

D

因为 \(F\) 位于直径上且具有长度限制,所以很容易想到双指针维护 \(F\).

求出 \(F\) 之后,设其上端点为 \(i\),下端点为 \(j\),那么它的 \(\text{ECC}\) 存在三种情况:

  • 直径的下端点到 \(j\) 的长度

  • 直径上端点到 \(i\) 的长度

  • 直径外部的点连到 \(F\) 的长度.

前两个维护 \(F\) 的时候可以顺便求出,而第三个则可以对直径上每个点跑一遍 DFS 求出最长距离即可.三者取 \(\max\) 即为答案.时间复杂度 \(\mathcal{O}(n)\).

实现
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <stdlib.h>
#include <vector>
#include <queue>
#include <cmath>
#include <stack>
#include <map>
#include <set>
#define int long long
using namespace std;const int N=1e3+5;
int n,s;
int dis[N],f[N];
bool vis[N];
struct NODE{int v,w;
};
vector<NODE> G[N];void dfs(int cur,int fa){f[cur]=fa;for(auto [i,w]:G[cur]){if(i==fa||vis[i])continue;dis[i]=dis[cur]+w;dfs(i,cur);}
}signed main(){ios::sync_with_stdio(0);cin.tie(0);cin>>n>>s;for(int i=1,u,v,w;i<n;i++){cin>>u>>v>>w;G[u].push_back({v,w});G[v].push_back({u,w});}dfs(1,0);int p=0,ans=1e18;for(int i=1;i<=n;i++)if(dis[p]<dis[i])p=i;memset(dis,0,sizeof dis);dfs(p,0);for(int i=1;i<=n;i++)if(dis[p]<dis[i])p=i;for(int i=p,j=p;i;i=f[i]){vis[i]=1;for(;dis[j]-dis[i]>s;j=f[j]);ans=min(ans,max(dis[i],dis[p]-dis[j]));}for(int i=p;i;i=f[i])dis[i]=0,dfs(i,f[i]);for(int i=1;i<=n;i++)ans=max(ans,dis[i]);cout<<ans;return 0;
}

总结

这三个知识点对应的性质记得考前记背.

树上路径转序列先以一个端点进行 dfs.

http://www.hskmm.com/?act=detail&tid=34549

相关文章:

  • 关于无人巡航小车的学习笔记
  • 详细介绍:springboot+vue智慧旅游管理小程序(源码+文档+调试+基础修改+答疑)
  • 存算一体架构的先行者:RustFS在异构计算环境下的探索与实践
  • 2-SAT
  • CSP-S模拟10
  • CSP-S模拟赛加赛 比赛总结
  • 我要好好写博客了 - Milo
  • 洛谷P4735--最大异或和
  • DAPO代码实现浅析
  • [KaibaMath]1011 关于收敛数列保号性的证明
  • Appium 3.0:跨平台移动自动化测试框架全面解析
  • 赛前训练 12 extra 树上差分倍增
  • 塔吊施工人员操作合规性监测!思通数科 AI 卫士实时守护作业安全
  • Dos命令1
  • 题解:P1073 [NOIP 2009 提高组] 最优贸易
  • 吩咐
  • 互评五
  • 机器人技术新前沿:自动驾驶路径规划算法解析
  • 前端框架文档新思路:基于源码解析的自动化方案
  • 常用模板
  • C++ std::forwardT 的使用
  • tryhackme-预安全-网络基础知识-数据包和帧-07
  • 迈向零信任存储:基于RustFS构建内生安全的数据架构
  • 如果这就是人类脑海的话 雪白纸上划出血红层层痕迹 不如杀死这些记忆
  • 嗣澳——扫,墨依奥——描,希伊桉——线
  • 服务器被攻击!原因竟然是他?真没想到...
  • 得到的眼泪学会了哭泣 得到的悲伤缓慢摧残肉体 被所爱之人踩在地
  • 框架架构的多维赋能——论其对自然语言处理深层语义分析的影响与启示
  • 使用 robocopy 命令备份还原数据速度统计
  • 顺天地之自然