[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)的更多相关文章

  1. 算法笔记--树的直径 && 树形dp && 虚树 && 树分治 && 树上差分 && 树链剖分

    树的直径: 利用了树的直径的一个性质:距某个点最远的叶子节点一定是树的某一条直径的端点. 先从任意一顶点a出发,bfs找到离它最远的一个叶子顶点b,然后再从b出发bfs找到离b最远的顶点c,那么b和c ...

  2. 2014 Super Training #9 E Destroy --树的直径+树形DP

    原题: ZOJ 3684 http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3684 题意: 给你一棵树,树的根是树的中心(到其 ...

  3. HDU4514 湫湫系列故事——设计风景线 ——树的直径/树形dp+判环

    中文题面,给出一个图,问能不能成环,如果可以就输出YES.否则输出该树的直径. 这里的判环我们用路径压缩的并查集就能很快的判断出来,可以在输入的同时进行判断.这题重点就是求树的直径. 树直径的性质可以 ...

  4. Codeforces 633F 树的直径/树形DP

    题意:有两个小孩玩游戏,每个小孩可以选择一个起始点,并且下一个选择的点必须和自己选择的上一个点相邻,问两个选的点权和的最大值是多少? 思路:首先这个问题可以转化为求树上两不相交路径的点权和的最大值,对 ...

  5. 【碳硫磷模拟赛】消失的+和* (树形DP)

    好久没做过这么恶心的DP题了 题面 题面很简单,有一个计算式,由+号.*号.括号和小于10的正整数组成,现在所有的+和*(由于属于违禁词而)都被-号给和谐掉了,现在要求所有可能的原计算式的结果之和. ...

  6. 5.21 省选模拟赛 luogu P4297 [NOI2006]网络收费 树形dp

    LINK:网络收费 还是自己没脑子. 早上思考的时候 发现树形dp不可做 然后放弃治疗了. 没有合理的转换问题的模型是我整个人最大的败笔. 暴力也值得一提 爆搜之后可以写成FFT的形式的计算贡献的方法 ...

  7. POJ 1849 Two(树的直径--树形DP)(好题)

    大致题意:在某个点派出两个点去遍历全部的边,花费为边的权值,求最少的花费 思路:这题关键好在这个模型和最长路模型之间的转换.能够转换得到,全部边遍历了两遍的总花费减去最长路的花费就是本题的答案,要思考 ...

  8. [10.18模拟赛] 序列 (DP)

    [10.18模拟赛] 序列 题目描述 山山有一个整数序列s1,s2,-,sn,其中1≤si≤k. 求出有多少个准确移除m个元素后不同的序列.答案模(1e9+7) 输入 输入包括几个测试用例,并且由文件 ...

  9. 4.11 省选模拟赛 序列 二分 线段树优化dp set优化dp 缩点

    容易想到二分. 看到第一个条件容易想到缩点. 第二个条件自然是分段 然后让总和最小 容易想到dp. 缩点为先:我是采用了取了一个前缀最小值数组 二分+并查集缩点 当然也是可以直接采用 其他的奇奇怪怪的 ...

随机推荐

  1. 181. Flip Bits【LintCode, by java】

    Description Determine the number of bits required to flip if you want to convert integer n to intege ...

  2. solidity事件详解

    很多同学对Solidity 中的Event有疑问,这篇文章就来详细的看看Solidity 中Event到底有什么用? 写在前面 Solidity 是以太坊智能合约编程语言,阅读本文前,你应该对以太坊. ...

  3. 使用Python进行AES加密和解密

    摘录于:http://blog.csdn.net/nurke/article/details/77267081 另外参考:http://www.cnblogs.com/kaituorensheng/p ...

  4. 软工1816 · Alpha冲刺(1/10)

    团队信息 队名:爸爸饿了 组长博客:here 作业博客:here 组员情况 组员1(组长):王彬 过去两天完成了哪些任务 前后端代码规范统一 针对之前的alpha冲刺安排进一步细化任务卡片 明确apl ...

  5. Debian实验机 常用命令

    1.开启中文输入法 fcitx 2. 开启无线连接 wicd 3. 远程连接 ssh root@XXX.XXX.XXX.XXX 4. 启动Ulipad ~/ulipad-master# python ...

  6. 3dContactPointAnnotationTool开发日志(七)

      调了半天发现是逻辑错误,改了一下终于没那么奇怪了:   但是有的接触点很明显跑偏了.再回顾一下自己是怎么求的,我是直接用的下面的代码求解一个点是否在另一个物体内部: var bounds = us ...

  7. 菜鸟的飞翔日记-os篇

    一轮王道os复习感想 1概述 虽然去年有上操作系统这门必修课,考的成绩也算理想,本来还有点沾沾自喜,嗯,觉得自己学的还不错,知道有一天我拿起了王道,(没给王道打广告)看王道的原因完全在于为考研做准备, ...

  8. 201621123033 《Java程序设计》第5周学习总结

    1. 本周学习总结 1.1 写出你认为本周学习中比较重要的知识点关键词 接口 Comparable Comparator 1.2 尝试使用思维导图将这些关键词组织起来.注:思维导图一般不需要出现过多的 ...

  9. PHP连接Redis操作函数

    phpredis是php的一个扩展,效率是相当高有链表排序功能,对创建内存级的模块业务关系 很有用;以下是redis官方提供的命令使用技巧: 下载地址如下: https://github.com/ow ...

  10. 阻塞 , 非阻塞 , 同步 ,异步 , I/O模型

    •阻塞,非阻塞:进程/线程要访问的数据是否就绪,进程/线程是否需要等待: •同步,异步:访问数据的方式,同步需要主动读写数据,在读写数据的过程中还是会阻塞:异步只需要I/O操作完成的通知,并不主动读写 ...