树的直径新求法、codeforces 690C3 Brain Network (hard)
树的直径新求法
讲解题目
今天考了一道题目,下面的思路二是我在考场上原创,好像没人想到这种做法,最原始的题目,考场上的题目是这样的:
你现在有1 个节点,他的标号为1,每次加入一个节点,第i 次加入的节点标号为i+1,且每次加入的节点父亲为已经存在的节点。你需要在每次加入节点后输出当前树的直径。
大意:给你$n$个点,最开始时只有一个点,一号节点,然后每一次把第$i$号节点和编号比他小的节点相连,连接一个点后,求树的直径。
样例输入
第一行一个整数n。接下来n-1 行,第i+1 行一个数表示标号为i+1 的点的父亲。
6
1
2
2
1
5
样例输出
一行n-1 个数,第i 个数表示加入i+1 号点后树的直径。
1 2 2 3 4
思路
先说一下常规的做法,我们看一下题目,会发现一个性质,很好的性质。在每一次加点之后,只有三种情况产生,第一种就是几点之后没有什么用,树的直径还是之前那个;第二种情况就是当前点和上一次的一个端点组成;而最后一种和第二种一样,只是是和另一个端点。用字母表示一下就是:设当前直径的表示方法为$(x1,x2)$,表示的是以$x1,x2$为两个端点的链。设新加进来的点为$y$,则新产生的树的直径只有三种情况:$(x1,y)$、$(y,x2)$、$(x1,x2)$。只需要维护一个倍增lca 和每个点的深度就行啦。这个不难想到(对与大佬们来说,像我这样的蒟蒻就没想到)。
虽然我没有想到,但是我自创了另一种做法。我们设$f[i]$表示的是以$i$号节点为根的子树中以$i$为端点的最长长度。这样我们计算答案时就很好计算了,我们只需要把新加进来的点旋转到整棵树的根,并更新$f$数组的值,把新加进来的点的$f$数组的值和上一个直径进行对比,去最大值就可以啦。难点就是旋转,大家还记的splay的旋转吗,这道题的旋转和splay的旋转的思路差不多,都是把儿子旋上去。但是相对而言这个更简单,因为我们不需要存他的儿子有谁,只需要存父亲就好了,但是我们发现旋转的时候旋下来的点的$f$数组的值会改变,所以我们需要重新更新。怎么更新呢?我们每一次都在和当前旋下来的点有一条边相连的所有点中选取$f$值最大的,并加一赋值,但有一个细节,就是这些点中不能包括要选上去的点。每一次更新就好啦。
时间分析
可能有人会问,这个不是$O(N^2)$的时间复杂度吗?虽然是期望$log_2^n$层,但是数据卡一卡就能卡成链,退化成$N^2$的算法啦。但是不要忘记,我们是有旋转操作的,旋来旋去,就成了一棵类似于平衡树的东西,因为你也不知道怎么旋,在没看代码的情况下,所以数据你做不出来,哈哈。再想一想,splay的精妙之处不也是这里吗?因为旋转成了平衡树。结束,时间复杂的$O(n*log_2^n)$。是不是很好?
代码
#include <stdio.h>
#include <algorithm>
using namespace std;
#define N 200001
int head[N],to[N*2],nxt[N*2];
int ans,idx,n;
int f[N];
int lenth[N];
void add(int a,int b)
{nxt[++idx]=head[a],head[a]=idx,to[idx]=b;}
void change(int p,int from)
{
if(f[p]) change(f[p],p);
lenth[p]=0;
for(int i=head[p];i;i=nxt[i])
if(to[i]!=from)
lenth[p]=max(lenth[p],lenth[to[i]]+1);
f[p]=from;
}
int main()
{
scanf("%d",&n);
for(int i=2;i<=n;i++)
{
scanf("%d",&f[i]),add(f[i],i),add(i,f[i]);
change(f[i],i),lenth[i]=lenth[f[i]]+1,f[i]=0;
ans=max(ans,lenth[i]);
printf("%d ",ans);
}
}
有不懂得,可以发评论提问,这种做法纯属原创。
树的直径新求法、codeforces 690C3 Brain Network (hard)的更多相关文章
- codeforces 690C3 Brain Network
simple:并查集一下 #include <vector> #include <iostream> #include <queue> #include <c ...
- 树的直径的求法即相关证明【树形DP || DFS】
学习大佬:树的直径求法及证明 树的直径 定义: 一棵树的直径就是这棵树上存在的最长路径. 给定一棵树,树中每条边都有一个权值,树中两点之间的距离定义为连接两点的路径边权之和.树中最远的两个节点之间的距 ...
- CF 690C3. Brain Network (hard) from Helvetic Coding Contest 2016 online mirror (teams, unrated)
题目描述 Brain Network (hard) 这个问题就是给出一个不断加边的树,保证每一次加边之后都只有一个连通块(每一次连的点都是之前出现过的),问每一次加边之后树的直径. 算法 每一次增加一 ...
- CodeForces 690C1 Brain Network (easy) (水题,判断树)
题意:给定 n 条边,判断是不是树. 析:水题,判断是不是树,首先是有没有环,这个可以用并查集来判断,然后就是边数等于顶点数减1. 代码如下: #include <bits/stdc++.h&g ...
- CodeForces 690C2 Brain Network (medium)(树上DP)
题意:给定一棵树中,让你计算它的直径,也就是两点间的最大距离. 析:就是一个树上DP,用两次BFS或都一次DFS就可以搞定.但两次的时间是一样的. 代码如下: #include<bits/std ...
- hdoj 4612 Warm up【双连通分量求桥&&缩点建新图求树的直径】
Warm up Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65535/65535 K (Java/Others)Total Su ...
- CodeForces 379F 树的直径 New Year Tree
题意:每次操作新加两个叶子节点,每次操作完以后询问树的直径. 维护树的直径的两个端点U,V,每次计算一下新加进来的叶子节点到U,V两点的距离,如果有更长的就更新. 因为根据树的直径的求法,若出现新的直 ...
- Gym - 100781A Adjoin the Networks (树的直径)
题意: n个点,m条边,m <= n <= 100000,边的长度都为1. 点从 0 ~ n-1 编号.开始时图是不连通的,并且没有环. 通过加入一些边后,可以使图连通.要求加入的边不能多 ...
- 树的直径&树的重心
树的直径 定义 那么树上最远的两个点,他们之间的距离,就被称之为树的直径. 树的直径的性质 1. 直径两端点一定是两个叶子节点. 2. 距离任意点最远的点一定是直径的一个端点,这个基于贪心求直径方法的 ...
随机推荐
- Spark性能优化:资源调优篇
在开发完Spark作业之后,就该为作业配置合适的资源了.Spark的资源参数,基本都可以在spark-submit命令中作为参数设置.很多Spark初学者,通常不知道该设置哪些必要的参数,以及如何设置 ...
- jar包导入仓库中
mvn install:install-file -Dfile=F:/kaptcha-2.3.jar -DgroupId=com.google.code.kaptcha -DartifactId=ka ...
- 我给女朋友讲编程总结建议篇,怎么学习html和css
总共写了11篇博客了,7篇讲html的,4篇讲网络的.不敢说写的多么好吧,最起码的是我迈出了写作的第一步,写作的过程中了解了一些其他的知识,比如SEO.几种重定向等,由于个人能力和见识有限,写出来的东 ...
- Python框架之Django的相册组件
Python框架之Django的相册组件 恩,没错,又是Django,虽然学习笔记已经结贴,但是学习笔记里都是基础的,Django的东西不管怎么说还是很多的,要学习的东西自然不会仅仅用十几篇博文就能学 ...
- Python基础-week02 Python的常用数据类型
一.模块初识 import导入Py自带模块例如os,sys等及其自己编写的Py文件,导入到其他文件中,默认查找当前目录.如果不在同一目录,会报错,将该自定义py文件模块放到site-packages目 ...
- python-生成器迭代器及递归调用
生成器是一个可迭代的对象,它的执行会记住上一次返回时在函数体中的位置.对生成器第二次(或第 n 次)调用跳转至该函数上次执行位置继续往下执行,而上次调用的所有局部变量都保持不变. 生成器的特点:1.生 ...
- 设计模式(一)单例模式:2-懒汉模式(Lazy)
思想: 相比于饿汉模式,懒汉模式实际中的应用更多,因为在系统中,“被用到时再初始化”是更佳的解决方案. 设计思想与饿汉模式类似,同样是持有一个自身的引用,只是将 new 的动作延迟到 getinsta ...
- java.net.BindException: Permission denied
端口号报错: 解决办法:把端口号改为1000以上的,比如8080
- EPEL 安装源
EPEL 安装源 EPEL 是 Extra Packages for Enterprise Linux 的缩写(EPEL),是用于 Fedora-based Red Hat Enterprise Li ...
- [51nod 1022] 石子归并v2 [dp+四边形不等式优化]
题面: 传送门 思路: 加强版的石子归并,现在朴素的区间dp无法解决问题了 首先我们破环成链,复制一条一样的链并粘贴到原来的链后面,变成一个2n长度的序列,在它上面dp,效率O(8n^3) 显然是过不 ...