题意

给一颗树,删除一条边再加一条边,使它仍为一颗树且任意两点间的距离的最大值最小。

题目数据范围描述有问题,n为1或重建不能使任意两点距离最大值变小,可以输出任意答案。

分析

删除一条边后会使它变成两颗树,两棵树的直径的中点相连一定是使距离最小的

红色的边为删除重建的边

在树上dp维护每个子树的最大直径\(h[x]\),和去除这个子树后的树的最大直径\(t[x]\),u为x的父亲,删除u-x这条边并重建后的树的最大直径为

\[\max\{\frac{h[x]+1}{2}+\frac{t[x]+1}{2}+1,h[x],t[x]\}
\]

设\(g[u]\)为以\(u\)为根的子树中\(u\)能到达的最远距离

设\(p[u]\)为去除以\(u\)为根的子树后\(u\)能到达的最远距离

自底向上

x为u的孩子,\(mx1\),\(mx2\)分别为\(g[x]\)的最大值和次大值

  • \(g[u]=max(g[x]+1)\)
  • \(h[u]=max\{h[x],g[u],mx1+mx2+2\}​\)

自顶向下

k为x的兄弟,\(mx1​\),\(mx2​\)分别为\(g[k]\)的最大值和次大值

  • \(p[x]=max(p[u]+1,g[k]+2)​\)
  • \(t[x]=max \{p[u],h[k],p[u]+g[k]+1,mx1+mx2+2 \}​\)

然后bfs找重建的边

实现细节很多,我写的比较乱,建议自己根据dp式子模拟一下

Code

#include<bits/stdc++.h>
#define fi first
#define se second
#define bug cout<<"--------------"<<endl
using namespace std;
typedef long long ll;
const double PI=acos(-1.0);
const double eps=1e-6;
const int inf=1e9;
const ll llf=1e18;
const int mod=1e9+7;
const int maxn=3e5+10;
int n;
vector<int>f[maxn];
typedef pair<int,int> pii;
pii e[maxn];
int g[maxn],h[maxn],p[maxn],t[maxn];
int ans=inf;
pii ans1,ans2;
void dfs1(int u,int fa){
int mx1=-inf,mx2=-inf;
int len=f[u].size();
int po=len;
for(int i=0;i<len;i++){
int x=f[u][i];
if(x==fa){
continue;
}
dfs1(x,u);
g[u]=max(g[x]+1,g[u]);
if(g[x]>mx1){
mx2=mx1;
mx1=g[x];
}else if(g[x]>mx2){
mx2=g[x];
}
h[u]=max(h[u],h[x]);
}
h[u]=max(h[u],g[u]);
h[u]=max(mx1+mx2+2,h[u]);
}
int pre[maxn],suf[maxn];
int pr[maxn],sf[maxn];
void dfs2(int u,int fa){
int len=f[u].size();
vector<int>q;
q.push_back(0);
for(int i=0;i<len+5;i++) pre[i]=suf[i]=pr[i]=sf[i]=-inf;
for(int i=0;i<len;i++){
int x=f[u][i];
if(x!=fa) q.push_back(x);
}
len=q.size()-1;
for(int i=1;i<=len;i++){
int x=q[i];
t[x]=max(t[x],t[u]);
pre[i]=max(pre[i-1],g[x]);
pr[i]=max(pr[i-1],h[x]);
}
for(int i=len;i>=1;i--){
int x=q[i];
suf[i]=max(suf[i+1],g[x]);
sf[i]=max(sf[i+1],h[x]);
}
for(int i=1;i<=len;i++){
int x=q[i];
p[x]=max(p[x],p[u]+1);
t[x]=max(p[u],t[x]);
}
for(int i=2;i<=len;i++){
int x=q[i];
t[x]=max(p[u]+1+pre[i-1],t[x]);
t[x]=max(pr[i-1],t[x]);
p[x]=max(pre[i-1]+2,p[x]);
}
for(int i=1;i<len;i++){
int x=q[i];
t[x]=max(p[u]+1+suf[i+1],t[x]);
t[x]=max(sf[i+1],t[x]);
p[x]=max(suf[i+1]+2,p[x]);
}
for(int i=2;i<len;i++){
int x=q[i];
t[x]=max(t[x],pre[i-1]+suf[i+1]+2);
}
int mx1=-inf,mx2=-inf;
for(int i=1;i<=len;i++){
int x=q[i];
t[x]=max(mx1+mx2+2,t[x]);
if(g[x]>mx1){
mx2=mx1;
mx1=g[x];
}else if(g[x]>mx2){
mx2=g[x];
}
}
mx1=mx2=-inf;
for(int i=len;i>=1;i--){
int x=q[i];
t[x]=max(mx1+mx2+2,t[x]);
if(g[x]>mx1){
mx2=mx1;
mx1=g[x];
}else if(g[x]>mx2){
mx2=g[x];
}
}
for(int i=1;i<=len;i++){
int x=q[i];
int dis=max(max(t[x],h[x]),(t[x]+1)/2+(h[x]+1)/2+1);
if(dis<ans){
ans=dis;
ans1=pii(x,u);
}
}
for(int i=0;i<(int)f[u].size();i++){
int x=f[u][i];
if(x==fa) continue;
dfs2(x,u);
}
}
int pe[maxn],vis[maxn];
queue<int>q;
int bfs(int fa){
int ret=fa;
memset(vis,0,sizeof(vis));
memset(pe,0,sizeof(pe));
q.push(fa);
vis[fa]=1;
while(!q.empty()){
int u=q.front();
q.pop();
ret=u;
int len=f[u].size();
for(int i=0;i<len;i++){
if(!vis[f[u][i]]){
q.push(f[u][i]);
pe[f[u][i]]=u;
vis[f[u][i]]=1;
}
}
}
return ret;
}
int fq[maxn],tot;
void dfs(int u,int s){
if(u==0) return;
fq[++tot]=u;
dfs(pe[u],s);
}
int find(int x){
tot=0;
int s=bfs(x);
int t=bfs(s);
dfs(t,s);
return fq[(tot+1)/2];
}
void work(){
for(int i=1;i<=n;i++){
f[i].clear();
}
for(int i=1;i<n;i++){
int a=e[i].fi,b=e[i].se;
if(a==ans1.fi&&b==ans1.se) continue;
if(b==ans1.fi&&a==ans1.se) continue;
f[a].push_back(b);
f[b].push_back(a);
}
ans2.fi=find(ans1.fi);
ans2.se=find(ans1.se);
cout<<ans<<endl;
cout<<ans1.fi<<" "<<ans1.se<<endl;
cout<<ans2.fi<<" "<<ans2.se<<endl;
}
int main(){
ios::sync_with_stdio(false);
//freopen("in","r",stdin);
cin>>n;
for(int i=1,a,b;i<n;i++){
cin>>a>>b;
f[a].push_back(b);
f[b].push_back(a);
e[i]=pii(a,b);
}
dfs1(1,0);
dfs2(1,0);
work();
return 0;
}

BZOJ 2651 城市改建 树形DP+模拟?的更多相关文章

  1. BZOJ 1149 风铃(树形DP)

    题目描述的实际是一颗二叉树,对于每个结点,要么满叉,要么无叉. 对于一种无解的简单情况,我们搜一遍树找到最浅的叶子结点1和最深的叶子结点2,如果dep[1]<dep[2]-1,则显然无解. 所以 ...

  2. bzoj 1369: Gem 树形dp

    题目大意 给出一棵树,要求你为树上的结点标上权值,权值可以是任意的正整数 唯一的限制条件是相临的两个结点不能标上相同的权值,要求一种方案,使得整棵树的总价值最小.N<=10000 题解 我们可以 ...

  3. 【BZOJ 3090】 树形DP

    3090: Coci2009 [podjela] Description 有 N 个农民, 他们住在 N 个不同的村子里. 这 N 个村子形成一棵树.每个农民初始时获得 X 的钱.每一次操作, 一个农 ...

  4. bzoj 1131 简单树形dp

    思路:随便想想就能想出来啦把...  卡了我一个vector... #include<bits/stdc++.h> #define LL long long #define fi firs ...

  5. bzoj 4987 Tree —— 树形DP

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4987 其实就是在树上找有 k 个点的连通块(路径上的点都选是最优的),之间的边都走了两遍,只 ...

  6. BZOJ 4753 二分+树形DP

    思路: 先二分答案 f[x][j]表示在x的子树里选j个点 f[x][j+k]=max(f[x][j+k],f[x][j]+f[v[i]][k]); 初始化 x!=0 -> f[x][1]=p[ ...

  7. [BZOJ 4033] [HAOI2015] T1 【树形DP】

    题目链接:BZOJ - 4033 题目分析 使用树形DP,用 f[i][j] 表示在以 i 为根的子树,有 j 个黑点的最大权值. 这个权值指的是,这个子树内部的点对间距离的贡献,以及 i 和 Fat ...

  8. [BZOJ 1907] 树的路径覆盖 【树形DP】

    题目链接:BZOJ - 1907 题目分析 使用树形 DP,f[x][0] 表示以 x 为根的子树不能与 x 的父亲连接的最小路径数(即 x 是一个折线的拐点). f[x][1] 表示以 x 为根的子 ...

  9. bzoj 4871: [Shoi2017]摧毁“树状图” [树形DP]

    4871: [Shoi2017]摧毁"树状图" 题意:一颗无向树,选两条边不重复的路径,删去选择的点和路径剩下一些cc,求最多cc数. update 5.1 : 刚刚发现bzoj上 ...

随机推荐

  1. Codeforces 1236A. Stones

    传送门 注意到两种操作都要消耗中间的石头,并且两种操作每次都会得到 $3$ 个石头 那么显然优先做操作二是最优的,因为操作二只会消耗中间堆的一个石头 如果有剩下再进行操作 $1$ ,那么可以保证总操作 ...

  2. Java 注解(原理及其使用)

    一.注解(annotation)介绍 Java在JDK5中引入源代码的注解机制. 1.什么是注解? 注解为代码添加了元数据,元数据是关于数据的组织.数据域及其关系的说明信息. 更通俗的说,注解为程序元 ...

  3. redis 学习(18)-- AOF

    redis -- AOF 什么是 AOF 通过日志方式将redis中的写命令进行日志记录,保存在硬盘文件中. 日志记录的实质是将写命令写在硬盘的缓冲区中,再根据相关策略把数据刷新到磁盘中. 当redi ...

  4. JSTreeShaking的webpack-deep-scope-plugin插件的应用

    webpack自身实现词法分析的JSTreeShaking webpack-depp-scope-plugin插件实现作用域分析的JSTreeShaking 一.webpack词法分析的JSTreeS ...

  5. 安装.Net Framework 2.0出现 Error 25015

    ---------------------------Microsoft .NET Framework 2.0 安装程序---------------------------Error 25015.安 ...

  6. docker 安装ps命令

    apt-get update && apt-get install procps

  7. 工作总结 select sum(A+B) from table select (A+B) from table

    select IsTop,ActivityGlXs from ActivityGlInfo 其他类似 可以在 原理 先字段值 相加 根据值 再聚合运算 其他聚合函数类似 总结: 表字段是 可以相加,相 ...

  8. source命令用法:

    命令格式 source FileName 作用 在当前bash环境下读取并执行FileName中的命令. 注意 该命令通常用命令"."来替代.如:source .bash_rc 与 ...

  9. BLE 5协议栈-链路层

    文章转载自:http://www.sunyouqun.com/2017/04/page/3/ 链路层LL(Link Layer)是协议栈中最重要的一层. 链路层的核心是状态机,包含广播.扫描.发起和连 ...

  10. C++第五次作业--运算符重载和函数重载

    C++ 运算符重载和函数重载 C++ 允许在同一作用域中的某个函数和运算符指定多个定义,分别称为函数重载和运算符重载. 重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明,但是 ...