$des$

给定一棵 n 个节点的树,你可以进行 n ? 1 次操作,每次操作步骤如下:
选择 u,v 两个度数为 1 的节点。
将 u,v 之间的距离加到 ans 上。
将 u 从树上删除。
求一个操作序列使得 ans 最大。

$sol$

先把直径拿出来,将直径外的点一个一个的和直径中的某一个端点配对并删掉。最
后再将直径删掉。这样就是对的。
如果当前直径外已经没有点了,那么显然只能将直径的端点删掉。否则一定不会去
删直径的端点。
因为先删一个直径外的点再删直径端点一定是不劣于先删直径端点再删这个直径外
的点的。

$code$

#include <bits/stdc++.h>

using namespace std;

#define gc getchar()
inline int read() {
int x = ; char c = gc;
while(c < '' || c > '') c = gc;
while(c >= '' && c <= '') x = x * + c - '', c = gc;
return x; } #define LL long long
#define Rep(i, a, b) for(int i = a; i <= b; i ++)
#define E exit(0) const int N = 2e5 + ; int disa[N], disb[N], fa[N], A[N], topp[N], size[N], son[N], deep[N], sonjs[N];
bool Be_calc[N], vis[N], is_chain[N];
int Cut[N], js; struct Node {int v, nxt;} G[N << ];
int head[N], cnt; int n, One, Tow; void Link(int u, int v) {
G[++ cnt].v = v; G[cnt].nxt = head[u]; head[u] = cnt;
} int Bfs(int start, int dis[]) {
queue <int> Q;
memset(vis, , sizeof vis);
dis[start] = ;
vis[start] = ;
Q.push(start);
while(!Q.empty()) {
int topp = Q.front();
Q.pop();
for(int i = head[topp]; ~ i; i = G[i].nxt) {
int v = G[i].v;
if(!vis[v]) {
dis[v] = dis[topp] + ;
vis[v] = ;
Q.push(v);
}
}
}
int ret, retdis = -;
Rep(i, , n) if(dis[i] > retdis) ret = i, retdis = dis[i];
return ret;
} void Dfs1(int u, int f_, int dep) {
fa[u] = f_, deep[u] = dep; size[u] = ;
for(int i = head[u]; ~ i; i = G[i].nxt) {
int v = G[i].v;
if(v == f_) continue;
sonjs[u] ++;
Dfs1(v, u, dep + );
size[u] += size[v];
if(size[v] > size[son[u]]) son[u] = v;
}
} void Dfs2(int u, int tp) {
topp[u] = tp;
if(!son[u]) {
Cut[++ js] = u;
return ;
}
Dfs2(son[u], tp);
for(int i = head[u]; ~ i; i = G[i].nxt) {
int v = G[i].v;
if(v != fa[u] && v != son[u]) Dfs2(v, v);
}
} inline int Lca(int x, int y) {
int tpx = topp[x], tpy = topp[y];
while(tpx != tpy) {
if(deep[tpx] < deep[tpy]) swap(x, y), swap(tpx, tpy);
x = fa[tpx], tpx = topp[x];
}
if(deep[x] < deep[y]) swap(x, y);
return y;
} void Find_chain() {
int lca = Lca(One,Tow);
int tmp1 = One, tmp2 = Tow;
while(tmp1 != lca) {
is_chain[tmp1] = ;
tmp1 = fa[tmp1];
}
while(tmp2 != lca) {
is_chain[tmp2] = ;
tmp2 = fa[tmp2];
}
is_chain[lca] = ;
} int o_1[N], o_2[N], o_3[N], tot; int main() {
n = read();
Rep(i, , n) head[i] = -;
Rep(i, , n - ) {
int u = read(), v = read(); Link(u, v), Link(v, u);
} One = Bfs(, disa);
Tow = Bfs(One, disa);
Bfs(Tow, disb);
Dfs1(One, , );
Dfs2(One, One);
Find_chain(); LL Answer = ;
Rep(i, , js) {
if(is_chain[Cut[i]] || Be_calc[Cut[i]]) continue;
int now = Cut[i]; while(!Be_calc[now] && !is_chain[now] && !sonjs[now]) {
if(disa[now] > disb[now]) {
o_1[++ tot] = One, o_2[tot] = now, o_3[tot] = now;
Answer += disa[now];
} else {
o_1[++ tot] = Tow, o_2[tot] = now, o_3[tot] = now;
Answer += disb[now];
}
Be_calc[now] = ;
now = fa[now];
sonjs[now] --;
}
} int lca = Lca(One, Tow); while(One != lca) {
o_1[++ tot] = One, o_2[tot] = Tow, o_3[tot] = One;
Answer += disb[One];
One = fa[One];
}
while(Tow != lca) {
o_1[++ tot] = Tow, o_2[tot] = lca, o_3[tot] = Tow;
Answer += (deep[Tow] - deep[lca]);
Tow = fa[Tow];
} cout << Answer << "\n";
Rep(i, , tot) {
cout << o_1[i] << " " << o_2[i] << " " << o_3[i] << "\n";
}
return ;
}

cf 911F 树的直径+贪心的更多相关文章

  1. Sonya and Ice Cream CodeForces - 1004E 树的直径, 贪心

    题目链接 set维护最小值贪心, 刚开始用树的直径+单调队列没调出来... #include <iostream>#include <cstdio> #include < ...

  2. [TJOI2017] 城市 (树的直径,贪心)

    题目链接 Solution 这道题,调了我一晚上... 一直80分 >_<|| ... 考虑到几点: 分开任意一条边 \(u\) ,那么其肯定会断成两棵树. 肯定是分开直径上的边最优,否则 ...

  3. [SDOI2013]直径 (树的直径,贪心)

    题目链接 Solution 我们直接找到一条直径 \(s\),起点为 \(begin\),终点为 \(end\). 从前往后遍历点 \(u\) ,若子树中最大的距离与 \(dis(u,begin)\) ...

  4. CF911F Tree Destruction (树的直径,贪心)

    题目链接 Solution 1.先找出树的直径. 2.遍历直径沿途的每一个节点以及它的子树. 3.然后对于每个非直径节点直接统计答案,令直径的两个端点为 \(x_1,x_2\) . \[Ans=\su ...

  5. [NOI2003]逃学的小孩 (贪心+树的直径+暴力枚举)

    Input 第一行是两个整数N(3 <= N <= 200000)和M,分别表示居住点总数和街道总数.以下M行,每行给出一条街道的信息.第i+1行包含整数Ui.Vi.Ti(1<=Ui ...

  6. CF 120F Spider 树的直径 简单题

    一个男孩有n只玩具蜘蛛,每只蜘蛛都是一个树的结构,现在男孩准备把这n只小蜘蛛通过粘贴节点接在一起,形成一只大的蜘蛛.大的蜘蛛也依然是树的结构.输出大的蜘蛛的直径. 知识: 树的直径是指树上的最长简单路 ...

  7. 【Cf #292 D】Drazil and Morning Exercise(树的直径,树上差分)

    有一个经典的问题存在于这个子问题里,就是求出每个点到其他点的最远距离. 这个问题和树的直径有很大的关系,因为事实上距离每个点最远的点一定是直径的两个端点.所以我们可以很容易地进行$3$遍$Dfs$就可 ...

  8. 牡丹江.2014B(图论,树的直径)

    B - Building Fire Stations Time Limit:5000MS     Memory Limit:131072KB     64bit IO Format:%lld & ...

  9. 与图论的邂逅01:树的直径&基环树&单调队列

    树的直径 定义:树中最远的两个节点之间的距离被称为树的直径.  怎么求呢?有两种官方的算法(不要问官方指谁我也不晓得): 1.两次搜索.首先任选一个点,从它开始搜索,找到离它最远的节点x.然后从x开始 ...

随机推荐

  1. response letter

    1.Firstly, we would like to thank you for your kind letter and for reviewers’ constructive commentsc ...

  2. idea 代码热启动配置方法

    Restart Server:代码已更新重启tomcat服务器

  3. Jmeter_自带脚本录制

    1.http请求+查看结果树 代理服务器操作步骤 1.创建一个线程组(右键点击“测试计划“---> ”添加“ ---> ”线程组“) 2.创建一个http代理服务器(右键“测试计划”--& ...

  4. Windows MySql增量备份、完整备份采坑之路

    1.前言 这周公司交给我一个任务,负责项目Mysql数据库的备份,因为项目上线后数据是一个大问题,出了什么问题数据才是最大的问题,备份这时候就显得尤为重要, 公司项目的思路是:在项目系统设置内可以勾选 ...

  5. android使用http3

    http3的github地址: https://github.com/cloudflare/quiche

  6. (一) CentOS 7 进行 Docker CE 安装

    参考并感谢 官方文档: https://docs.docker.com/install/linux/docker-ce/centos/ 卸载旧版本 # 停止所有正在运行的容器 docker stop ...

  7. c#自制抽奖小程序

    #region 第一部分界面设计 ; Button button = new Button(); Image[] images = new Image[N]; PictureBox[] picture ...

  8. 并发编程-线程-死锁现象-GIL全局锁-线程池

    一堆锁 死锁现象 (重点) 死锁指的是某个资源被占用后,一直得不到释放,导致其他需要这个资源的线程进入阻塞状态. 产生死锁的情况 对同一把互斥锁加了多次 一个共享资源,要访问必须同时具备多把锁,但是这 ...

  9. 【新手可看懂】ubuntu配置appium环境

    1.node安装: 在node官网:https://nodejs.org/en/download/ 下载对应的安装包(这里建议下载最新的版本)下载好后放在liunx指定路径下 我这里放到opt这个文件 ...

  10. oracle批量操作

    https://stackoverflow.com/questions/39576/best-way-to-do-multi-row-insert-in-oracle 1 批量insert 方式一: ...