BZOJ 2282 & 树的直径
SDOI2011的Dayx第2题
题意:
在树中找到一条权值和不超过S的链(为什么是链呢,因为题目中提到“使得路径的两端都是城市”,如果不是链那不就不止两端了吗——怎么这么机智的感觉...),使得不在链上的点与这条链的距离最大值最小。
SOL:
最大值最小!这不是二分的节奏么?然而hzw学长说二分更直观我却一点都没有体会到...
这道题的关键是猜想(貌似还挺好想)并证明(貌似一直都是可有可无的东西,不过还挺好证的),路径一定在直径上,那么我们先两遍*FS找到直径,用一个队列维护链上的路径,以及预处理出直径上的点其子树中距离最远的点的距离,再维护路径两头距离直径的两端的距离即可,答案即这三者的最大值。
总感觉应该挺快的吧但是居然比他们二分答案的慢了几倍(A得窝囊),我也就是用了一个set而已(方便取出与删除然而我觉得可以再优化不用这个set...
关于证明:(自己口胡的证明,极其不严谨
证明还是非常好证的(也就自己yy),我们假设一条直径,假设最优的链不在这条直径上——我们可以把这条链看作一个点,那么可以证明它的最远点一定是直径的两个端点之一(否则树的直径就可以更长),那么显然这条链是包含直径的一部分的,再证全部包含,只要在部分包含的基础上对延伸进行讨论,就得证了吧...(感觉对了就好了不过是给自己a掉多找一个正当理由...)
CODE:
代码实现不是很难,然而我RE几次方了把DFS改成BFS又把自带queue改成手工队列——结果小细节错误百出...
/*==========================================================================
# Last modified: 2016-02-12
# Filename: 2016trainning1 t1.cpp
# Description:
==========================================================================*/
#define me AcrossTheSky
#include <cstdio>
#include <cmath>
#include <ctime>
#include <string>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm> #include <set>
#include <map>
#include <stack>
#include <queue>
#include <vector>
#define lowbit(x) (x)&(-x)
#define INF 1000000000
#define FOR(i,a,b) for((i)=(a);(i)<=(b);(i)++)
#define FORP(i,a,b) for(int i=(a);i<=(b);i++)
#define FORM(i,a,b) for(int i=(a);i>=(b);i--)
#define ls(a,b) (((a)+(b)) << 1)
#define rs(a,b) (((a)+(b)) >> 1)
#define check(ch) (ch>='0'&&ch<='9')
#define maxn 300001
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int ans=INF;
void read(int &t){
int ch;
for(ch=getchar( );!check(ch);ch=getchar( ));
t=ch-'';
for(ch=getchar( );check(ch);ch=getchar( ))t*=,t+=ch-'';
}
/*==================split line==================*/
int n,s,sume=,maxx;
struct Edge{
int to,len;
}e[maxn*];
struct cmp{
bool operator()(const int &x,const int &y) const{
return x>y;
}
};
int first[maxn],next[maxn*],nextn[maxn],dis[maxn],temp[],f[maxn],pre[maxn];
int q[maxn*];
bool vis[maxn];
set<int,cmp> b;
void addedge(int x,int y,int len){
sume++; e[sume].to=y; e[sume].len=len;
next[sume]=first[x]; first[x]=sume;
}
void bfs(int node,int d){
memset(q,,sizeof(q));
int front=,tail=; dis[node]=;
q[front]=node; vis[node]=true;
while (front<=tail){
int x=q[front];
front++;
if (dis[x]>maxx) maxx=dis[x],temp[d]=x;
for (int i=first[x];i!=-;i=next[i])
if (!vis[e[i].to]){
pre[e[i].to]=x;
dis[e[i].to]=dis[x]+e[i].len;
tail++; q[tail]=e[i].to; vis[e[i].to]=true;
}
}
}
void bfs1(int node,int root){
memset(q,,sizeof(q));
int front=,tail=; q[front]=node;
while (front<=tail){
int x=q[front]; front++; vis[x]=true;
f[root]=max(f[root],dis[x]-dis[root]);
for (int i=first[x];i!=-;i=next[i])
if (e[i].to!=nextn[node] && !vis[e[i].to]){
tail++; q[tail]=e[i].to;
}
}
}
int main(){
read(n); read(s);
FORP(i,,n) first[i]=-;
FORP(i,,n-) {
int x,y,l;
read(x); read(y); read(l);
addedge(x,y,l); addedge(y,x,l);
}
maxx=; bfs(,);
memset(vis,false,sizeof(vis));
maxx=; bfs(temp[],);
int L=temp[],R=temp[];
memset(vis,false,sizeof(vis));
for (int i=L;i!=R;i=pre[i]) nextn[pre[i]]=i;
L=temp[],R=temp[];
for (int i=L;i!=R;i=nextn[i]) { vis[i]=true; bfs1(i,i); }
vis[R]=true; bfs1(R,R);
int llen=,rlen=maxx,head=L,tail=L,len=; while (true){
if (head==R) break;
if (dis[nextn[head]]<s) {
len=dis[nextn[head]];
head=nextn[head];
b.insert(f[head]);
rlen=maxx-dis[head];
continue;
}
break;
} int t=max(rlen,max(llen,*b.begin()));
if (t<ans) ans=t;
while (head!=R){
len=dis[nextn[head]];
head=nextn[head];
rlen=maxx-dis[head];
while(len-dis[tail]>s){
b.erase(f[tail]); tail=nextn[tail]; llen=dis[tail];
}
t=max(rlen,max(llen,*b.begin()));
if (t<ans) ans=t;
}
printf("%d",ans);
}
BZOJ 2282 & 树的直径的更多相关文章
- bzoj 2282 [Sdoi2011]消防(树的直径,二分)
Description 某个国家有n个城市,这n个城市中任意两个都连通且有唯一一条路径,每条连通两个城市的道路的长度为zi(zi<=1000). 这个国家的人对火焰有超越宇宙的热情,所以这个国家 ...
- BZOJ 1912: [Apio2010]patrol 巡逻 (树的直径)(详解)
题目: https://www.lydsy.com/JudgeOnline/problem.php?id=1912 题解: 首先,显然当不加边的时候,遍历一棵树每条边都要经过两次.那么现在考虑k==1 ...
- 【BZOJ】1912: [Apio2010]patrol 巡逻(树的直径)
题目 传送门:QWQ 分析 $ k=1 $ 时显然就是树的直径 $ k=2 $ 时怎么做呢? 做法是把一开始树的直径上的边的边权改成$ -1 $,那么当我们第二次用这些边做环时就抵消了一开始的贡献. ...
- 【BZOJ】2657: [Zjoi2012]旅游(journey)(树的直径)
题目 传送门:QWQ 分析 在任意两个不相邻的点连一条线,求这条线能穿过几个三角形. 建图比较讲究(详见代码) 求树的直径. 代码 #include <bits/stdc++.h> usi ...
- bzoj 1776: [Usaco2010 Hol]cowpol 奶牛政坛——树的直径
农夫约翰的奶牛住在N (2 <= N <= 200,000)片不同的草地上,标号为1到N.恰好有N-1条单位长度的双向道路,用各种各样的方法连接这些草地.而且从每片草地出发都可以抵达其他所 ...
- BZOJ 3363 POJ 1985 Cow Marathon 树的直径
题目大意:给出一棵树.求两点间的最长距离. 思路:裸地树的直径.两次BFS,第一次随便找一个点宽搜.然后用上次宽搜时最远的点在宽搜.得到的最长距离就是树的直径. CODE: #include < ...
- D4 树的直径、重心以及基环树
第一题第二题鉴上我前几篇博客poj1985 poj1849:https://www.cnblogs.com/Tyouchie/p/10384379.html 第三题:数的重心:poj1655 来自sj ...
- 算法笔记--树的直径 && 树形dp && 虚树 && 树分治 && 树上差分 && 树链剖分
树的直径: 利用了树的直径的一个性质:距某个点最远的叶子节点一定是树的某一条直径的端点. 先从任意一顶点a出发,bfs找到离它最远的一个叶子顶点b,然后再从b出发bfs找到离b最远的顶点c,那么b和c ...
- [bzoj1791][ioi2008]Island 岛屿(基环树、树的直径)
[bzoj1791][ioi2008]Island 岛屿(基环树.树的直径) bzoj luogu 题意可能会很绕 一句话:基环树的直径. 求直径: 对于环上每一个点记录其向它的子树最长路径为$dp_ ...
随机推荐
- OpenGL Vertex Array
转载 http://blog.csdn.net/dreamcs/article/details/7699603
- haartraining生成.xml过程
继续向大神学习http://www.cnblogs.com/tornadomeet/archive/2012/03/28/2420936.html
- python 异步线程简单实现
import threading def foo(): with open(r'./result.log','wb') as f: f.write('=some logs here ==') t = ...
- linux 下如何查看和踢除正在登陆的其它用户 ==>Linux下用于查看系统当前登录用户信息的4种方法
在linux系统中用pkill命令踢出在线登录用户 由于linux服务器允许多用户登录,公司很多人知道密码,工作造成一定的障碍 所以需要有时踢出指定的用户 1/#who 查出当前有那些终端登录(用 ...
- 【云计算】Dockerfile、镜像、容器快速入门
Dockerfile.镜像.容器快速入门 1.1.Dockerfile书写示例 Dockerfile可以用来生成Docker镜像,它明确的定义了Image的生成过程.虽然直接修改容器也可以提交生成镜像 ...
- 使用clssneme改变图片或样式
<title>使用className改变样式</title> <style type="text/css"> li{ font-size: 12 ...
- Django之表单字段的选填与后台界面的管理
参考: http://www.crazyant.net/1005.html http://gmingzhe.blog.51cto.com/810664/163051 所有的字段,默认blank=Fal ...
- 3.前端笔记之JavaScript基础
作者:刘耀 部分内容参考一下链接 参考: http://www.cnblogs.com/wupeiqi/articles/5369773.html http://javascript.ruanyife ...
- Greedy:三角形问题
题目大意:有n根长度的为a1,a2....an的棒子,如果棒子可以组成三角形,求这些棒子能组成的三角形的最大周长? 这一题,一般人只能想到三重循环,当然我们是CS专业的,不能这样想,其实这题可以用DP ...
- CentOS 6.5 下安装 Elasticsearch 5
安装最新的 Elasticsearch 5 需要Java 8.所有先要确定环境中是否有Java 8.如果没有则需要安装. 1. 安装Java 8 首先使用 yum list installed | g ...