[10.12模拟赛] 老大 (二分/树的直径/树形dp)
[10.12模拟赛] 老大
题目描述
因为 OB 今年拿下 4 块金牌,学校赞助扩建劳模办公室为劳模办公室群,为了体现 OI 的特色,办公室群被设计成了树形(n 个点 n − 1 条边的无向连通图),由于新建的办公室太大以至于要将奖杯要分放在两个不同的地方以便同学们丢硬币进去开光,OB 想请你帮帮他看看奖杯放在哪两个办公室使得在任意一个在劳模办公室做题的小朋友能最快地找到奖杯来开光。
一句话题意:给出一个 n 个点的树,在两个合适且不同的点放上奖杯,使得每个点到最近的奖杯距离最大值最小。
输入
第一行,一个整数 n。
接下来的 n − 1 行,每行两个数 x y
输出
一个数,表示最小的最大距离。
样例输入
8
1 2
1 3
2 4
2 5
3 6
3 7
1 8
样例输出
2
提示
对于前 60% 的数据,n ≤ 100。
对于前 80% 的数据,n ≤ 2000。
对于 80% 的数据,保证树的形态随机。
对于 100% 的数据,保证 3 ≤ n ≤ 200000。
Solution
这道题解决方法非常多,然而博主蒟蒻只会\(O(n\log n)\)的做法
那么这两个奖杯到底要放哪里呢?可以证明一定在树的直径上
简易的讲一下
这就要说到树的直径的性质:树的直径是树上最长的一条路径,且树上任意一个点距它距离最远的点一定是树的直径的一个端点
假设奖杯在树的直径上能满足条件,那么我们最起码要保证它一定能覆盖到直径的至少一个端点,否则肯定不满足条件,既然它能满足端点,那么一定能满足直径上的点的子树中的节点,除非一个点子树中最深深度比它离直径端点的距离还远,但这又违反了直径的性质,故假设成立
知道了这两个点在直径上,怎么知道它们距离直径的距离呢?因为这两个点离直径端点的距离肯定是在满足题意的前提下距离最大的,我们要让这个距离最小,可以二分这个距离,然后去\(O(n)\)验证
怎么验证,就是我开始说的性质,首先找到这两个节点,然后找两个节点之间的那一段区间的每一颗子树,看不在直径上的节点的最大深度是不是超过了我们二分的这个mid
考场上匆忙打出来的代码,有点丑陋,将就着看吧~~
听说这道题还可以树形dp\(O(n)\)做,在这里贴一下题解说的各种做法
\(3.1\ 60\% O(n^3 )\)
\(n^2\)枚举两个奖杯位置,再\(O(n)\)扫一遍看看每个位置离最近奖杯最远是多少。
\(3.2\ 80\% O(n^2)\)
考虑两个奖杯管辖的区域必定有一个边界,我们枚举这个边界,也就是一条边,其中一部分是子树,一部分是子树外,我们只要分别求出另外两个树的直径。
\(3.3\) 树形态随机
期望树的直径很短,两个奖杯都在直径上枚举。
\(3.4\ 100\%\) 二分答案1 \(O(nlogn)\)
奖杯在直径上,二分答案后取离直径上离端点距离答案的点,遍历 check 一遍。
\(3.5\ 100\%\) 二分答案 2 \(O(nlogn)\)
随便提一个节点为根,二分答案,深度最深的节点一定要被照顾到,所以最深的点往上跳答案层即可,和其距离答案以内的点都删掉,再做一次。
此法可以拓展到 k 个奖杯,由皮皮轩友情提供。
\(3.6\ 100\%\) 树形dp\(\ O(n)\)
在 80 分的基础上用树形 dp,记下每个点向下前三长和向上一格后不回该子树最长的路径长度。子树内直径是前两长的和与该子树各个子树直径取 max;子树外直径是父节点向上一格后不回该子树最长的路径长度,前两长不进入该子树的向下最长路径这三条取前两长加起来与父节点以上的答案取 max。
Code
#include<bits/stdc++.h>
#define rg register
#define il inline
#define Min(a,b) (a)<(b)?(a):(b)
#define Max(a,b) (a)>(b)?(a):(b)
#define lol long long
#define in(i) (i=read())
using namespace std;
const int N=2e5+10;
int read() {
int ans=0,f=1; char i=getchar();
while(i<'0' || i>'9') {if(i=='-') f=-1; i=getchar();}
while(i>='0' && i<='9') ans=(ans<<1)+(ans<<3)+i-'0',i=getchar();
return ans*=f;
}
int n,cur,s,t;
int to[N<<1],nex[N<<1],head[N];
int dis[N],f[N],son[N],vis[N],dep[N];
void add(int a,int b) {
to[++cur]=b,nex[cur]=head[a],head[a]=cur;
to[++cur]=a,nex[cur]=head[b],head[b]=cur;
}
void dfs(int u,int fa) {
f[u]=fa;
for(int i=head[u];i;i=nex[i]) {
if(to[i]==fa) continue;
dis[to[i]]=dis[u]+1;
dfs(to[i],u);
}
}
int dfs2(int u,int ans=1) {
dep[u]=dis[u];
for(int i=head[u];i;i=nex[i]) {
if(to[i]==f[u] || vis[to[i]]) continue;
dfs2(to[i]); dep[u]=max(dep[u],dep[to[i]]);
}return ans;
}
bool check(int mid) {
int a=s,b=t;
while(a!=t) {
if(dis[a]-dis[s]==mid) break;
a=son[a];
}
while(b!=s) {
if(dis[t]-dis[b]==mid) break;
b=f[b];
}
if(dis[b]-dis[a]>2*mid) return 0;
int ll=a,rr=b;
while(a!=b && b) {
dfs2(b); int AQ=dep[b]-dis[b];
if(min(dis[b]-dis[ll],dis[rr]-dis[b])+AQ>mid) return 0;
b=f[b];
}
dfs2(b); int AQ=dep[b]-dis[b];
if(min(dis[b]-dis[ll],dis[rr]-dis[b])+AQ>mid) return 0;
return 1;
}
int main()
{
//freopen("ob.in","r",stdin);
//freopen("ob.out","w",stdout);
in(n);
for(int i=1,a,b;i<n;i++)
in(a),in(b),add(a,b);
dfs(1,0);
for(int i=1;i<=n;i++) if(dis[i]>dis[s]) s=i;
memset(dis,0,sizeof(dis)); dfs(s,0);
for(int i=1;i<=n;i++) if(dis[i]>dis[t]) t=i;
int id=t; while(id) vis[id]=1,son[f[id]]=id,id=f[id];
vis[s]=vis[t]=1;
int l=0,r=dis[t];
while(l<r) {
int mid=l+r>>1;
if(check(mid)) r=mid;
else l=mid+1;
}cout<<r<<endl;
}
[10.12模拟赛] 老大 (二分/树的直径/树形dp)的更多相关文章
- 算法笔记--树的直径 && 树形dp && 虚树 && 树分治 && 树上差分 && 树链剖分
树的直径: 利用了树的直径的一个性质:距某个点最远的叶子节点一定是树的某一条直径的端点. 先从任意一顶点a出发,bfs找到离它最远的一个叶子顶点b,然后再从b出发bfs找到离b最远的顶点c,那么b和c ...
- 2014 Super Training #9 E Destroy --树的直径+树形DP
原题: ZOJ 3684 http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3684 题意: 给你一棵树,树的根是树的中心(到其 ...
- HDU4514 湫湫系列故事——设计风景线 ——树的直径/树形dp+判环
中文题面,给出一个图,问能不能成环,如果可以就输出YES.否则输出该树的直径. 这里的判环我们用路径压缩的并查集就能很快的判断出来,可以在输入的同时进行判断.这题重点就是求树的直径. 树直径的性质可以 ...
- Codeforces 633F 树的直径/树形DP
题意:有两个小孩玩游戏,每个小孩可以选择一个起始点,并且下一个选择的点必须和自己选择的上一个点相邻,问两个选的点权和的最大值是多少? 思路:首先这个问题可以转化为求树上两不相交路径的点权和的最大值,对 ...
- 【碳硫磷模拟赛】消失的+和* (树形DP)
好久没做过这么恶心的DP题了 题面 题面很简单,有一个计算式,由+号.*号.括号和小于10的正整数组成,现在所有的+和*(由于属于违禁词而)都被-号给和谐掉了,现在要求所有可能的原计算式的结果之和. ...
- 5.21 省选模拟赛 luogu P4297 [NOI2006]网络收费 树形dp
LINK:网络收费 还是自己没脑子. 早上思考的时候 发现树形dp不可做 然后放弃治疗了. 没有合理的转换问题的模型是我整个人最大的败笔. 暴力也值得一提 爆搜之后可以写成FFT的形式的计算贡献的方法 ...
- POJ 1849 Two(树的直径--树形DP)(好题)
大致题意:在某个点派出两个点去遍历全部的边,花费为边的权值,求最少的花费 思路:这题关键好在这个模型和最长路模型之间的转换.能够转换得到,全部边遍历了两遍的总花费减去最长路的花费就是本题的答案,要思考 ...
- [10.18模拟赛] 序列 (DP)
[10.18模拟赛] 序列 题目描述 山山有一个整数序列s1,s2,-,sn,其中1≤si≤k. 求出有多少个准确移除m个元素后不同的序列.答案模(1e9+7) 输入 输入包括几个测试用例,并且由文件 ...
- 4.11 省选模拟赛 序列 二分 线段树优化dp set优化dp 缩点
容易想到二分. 看到第一个条件容易想到缩点. 第二个条件自然是分段 然后让总和最小 容易想到dp. 缩点为先:我是采用了取了一个前缀最小值数组 二分+并查集缩点 当然也是可以直接采用 其他的奇奇怪怪的 ...
随机推荐
- Android 修改系统默认density
如你所知在Anroid N 中,系统添加了多个级别的密度值供用户选择. 系统的默认的值就是 ro.sf.lcd_density 同时其他级别的默认值的大小基础也是以默认值为基础,然后乘以不同的比例得到 ...
- 浅谈如何写出一个让(坑)人(王)很(之)难(王)发现的bug
该文章内容来自脚本之家,原文链接:https://www.jb51.net/news/598404.html 程序员的日常三件事:写bug.改bug.背锅.连程序员都自我调侃道,为什么每天都在加班?因 ...
- 对Java对象的认识与理解
今天是我学习编程以来第一次写博客,记下平日学习所得,本来这几日都在学习web框架 但觉得梳理一下之前所学很有必要.毕竟之前学习Java感觉很粗略只是以考试为目的.所以就以<Thinking in ...
- Grid 网格布局
CSS 网格布局(Grid Layout) 是CSS中最强大的布局系统. 这是一个二维系统,这意味着它可以同时处理列和行,不像 flexbox 那样主要是一维系统. 你可以通过将CSS规则应用于父元素 ...
- 一个五位数ABCDE乘以9,得到EDCBA,求此五位数
此题是面试时某面试官突然抛出的,要求逻辑分析推导,不许编码,5分钟时间算出来最终结果,当然,最终没有完全推算出来 下面是编码实现 #一个五位数ABCDE*9=EDCBA,求此数 for a in ra ...
- springMVC第二章
springMVC第二章 一.URL 映射 可以同时设置多个URL来访问某个控制器或方法.设置value属性: @RequestMapping(value= {"/grade",& ...
- LVS+Keepalive+Nginx实现负载均衡
本文参考:http://blog.csdn.net/yinwenjie/article/details/47211551 简单粗暴写一下,做备忘,刚刚搭好没做优化呢,后期补充 一.机器准备 LVS-M ...
- c#积累之测试
初来上班,免不了看别人代码.快速搞懂别人代码是我现在受到的一大挑战.寻摸着规律,发现一边进行调试,一边进行行行注释的逻辑判断不失为一种妙招. c#调试用的是vs2012.f11键和f10和f5键的应用 ...
- 3dContactPointAnnotationTool开发日志(十二)
因为ReferenceImage的锚点是固定的左下角,缩放时controller面板也会跟着动.为了使Scale的时候controller上的slider不会远离指针,于是把controller固 ...
- phpcms V9如何判断用户是否登录以及登陆后的标签写法问题
首先要获取userid {php$userid=param::get_cookie('_userid');} 然后再判断是否为空 {if $userid}...这里写已经登录之后的代码...{els ...