直径上的乱搞 bzoj1999求树直径上的结点+单调队列,bzoj1912负权树求直径+求直径边
直径上的乱搞一般要求出这条直径上的点集或者边集
bzoj1999:对直径上的点集进行操作
/*
给出一颗树,在树的直径上截取长度不超过s的路径
定义点u到s的距离为u到s的最短路径长度
定义s的偏心距为所有点到s的最大距离
定义树网的核为偏心距最小的s
给定s,请求出最小偏心距 题目中的结论:树的直径不唯一,但所有直径必定相交于直径的中点
推论:任意直径上求出的最小偏心距都相等
将树转化成另一个模型:即所有直径以外的分支都挂载在直径左右侧,
提取出直径,设直径上的结点u1,u2,u3...ut
那么用i,j两个指针,i从u1开始,j一直在直径上右移直到uj距离ui不超过s,
用单调队列维护在挂载在区间[i,j]的最大分支长度,那么这一段区间对应的偏心距就是
max(max{d(uk)},dist(u1,ui),dist(uj,ut)) ,k在区间[i,j]内
d数组是uk挂载的分支最大深度
由于没有负权,可以用dfs跑两遍求出直径,并用pre数组将直径记录下来
然后从每个直径上的点出发再进行一次dfs求出这个直径上挂载的分支的深度
然后用单调队列维护结果即可
*/
#include<bits/stdc++.h>
using namespace std;
#define maxn 500005
struct Edge{int to,nxt,w;}edge[maxn<<];
int head[maxn],tot,n,s;
void init(){
memset(head,-,sizeof head);
tot=;
}
void addedge(int u,int v,int w){
edge[tot].nxt=head[u];edge[tot].to=v;edge[tot].w=w;
head[u]=tot++;
}
int dep[maxn],fa[maxn],is[maxn],dis[maxn];
void dfs(int u,int pre,int deep){//返回深度最大的点
dep[u]=deep;fa[u]=pre;
for(int i=head[u];i!=-;i=edge[i].nxt){
int v=edge[i].to;
if(v==pre)continue;
dfs(v,u,deep+edge[i].w);
}
}
int dfs2(int u,int pre){//求直径上的点挂载的分支的深度
int res=;
for(int i=head[u];i!=-;i=edge[i].nxt){
int v=edge[i].to;
if(v==pre || is[v])continue;
res=max(dfs2(v,u)+edge[i].w,res);
}
return res;
} int main(){
init();
cin>>n>>s;
for(int i=;i<n;i++){
int u,v,w;cin>>u>>v>>w;
addedge(u,v,w);addedge(v,u,w);
}
//两次dfs求出直径
int S=;dfs(,,);
for(int i=;i<=n;i++)
if(dep[S]<dep[i])S=i;
memset(dep,,sizeof dep);
int T=;dfs(S,,);
for(int i=;i<=n;i++)
if(dep[T]<dep[i])T=i; // cout<<s<<" "<<t<<endl; fa[S]=;
for(int i=T;i;i=fa[i])is[i]=;//标记直径上的点
for(int i=T;i;i=fa[i])dis[i]=dfs2(i,fa[i]);//求出直径上挂载的分支的最大深度 //单调队列
int q[maxn]={},l=,r=,j=T,ans=<<;
q[]=T;
for(int i=T;i;i=fa[i]){
while(fa[j]!= && dep[i]-dep[fa[j]]<=s){//j往上移,把j加入单调队列
j=fa[j];
while(l<=r && dis[q[r]]<=dis[j])r--;
q[++r]=j;
ans=min(ans,max(dis[q[l]],max(dep[j]-dep[S],dep[T]-dep[i])));
}
while(q[l]==i)++l;
} cout<<ans<<endl;
}
bzoj1912 负权树求直径(只能用树形dp),然后记录直径上的边来回溯到两端点,同时将直径上的边权变负
/*
分情况讨论问题
一条边也不加的情况,显然每条边要扫描两次,
该情况的答案是2(n-1)
只加一条边的情况,找到直径,将其变成一个环,在这个环上的所有边只要扫描一次,剩下的边就要扫描两次
设直径为L,该情况下的答案是 2(n-1-L)+L+1=2n-L-1=2(n-1)-(L-1)
加两条边的情况,在加入第一条边出现环的情况下,再加入一条边形成的环会和原来的环有重合
重合的部分任然要扫描两次,所以新加入的边要使形成的环和原来的环非重合的边和重合的边的差最大
那么如何快速求新加入的边?只要将第一条边加入后形成的环的边权值变成-1,然后再求一次直径即可
设第一次的直径L1,第二次的直径L2,那么该情况下的答案是2(n-1)-(L1-1)-(L2-1)
*/
#include<bits/stdc++.h>
using namespace std;
#define maxn 100005
struct Edge{int to,nxt,w;}edge[maxn<<];
int head[maxn],tot,n,k,ans;
void init(){
memset(head,-,sizeof head);
tot=;
}
void addedge(int u,int v){
edge[tot].w=;edge[tot].to=v;edge[tot].nxt=head[u];head[u]=tot++;
} int pre1[maxn],pre2[maxn],dp[maxn],mx;//记录x的最长和次长子链的边的下标
int dfs(int x,int fa)
{
int mx1=,mx2=;
for(int i=head[x];i!=-;i=edge[i].nxt)
if(edge[i].to!=fa)
{
int v=edge[i].w+dfs(edge[i].to,x);
if(v>mx1)mx2=mx1,mx1=v,pre2[x]=pre1[x],pre1[x]=i;
else if(v>mx2)mx2=v,pre2[x]=i;
}
if(mx1+mx2>ans)ans=mx1+mx2,mx=x;
return mx1;
} int main(){
init();
memset(pre1,-,sizeof pre1);
memset(pre2,-,sizeof pre2);
cin>>n>>k;
int tot=*n-;
for(int i=;i<n;i++){
int u,v;cin>>u>>v;
addedge(u,v);addedge(v,u);
}
dfs(,);
tot=tot-ans+;
if(k==)
{
ans=;
for(int i=pre1[mx];i!=-;i=pre1[edge[i].to])edge[i].w=edge[i^].w=-;
for(int i=pre2[mx];i!=-;i=pre1[edge[i].to])edge[i].w=edge[i^].w=-;
dfs(,);tot=tot-ans+;
}
cout<<tot<<endl;
}
直径上的乱搞 bzoj1999求树直径上的结点+单调队列,bzoj1912负权树求直径+求直径边的更多相关文章
- 完美字符子串 单调队列预处理+DP线段树优化
题意:有一个长度为n的字符串,每一位只会是p或j.你需要取出一个子串S(注意不是子序列),使得该子串不管是从左往右还是从右往左取,都保证每时每刻已取出的p的个数不小于j的个数.如果你的子串是最长的,那 ...
- 单调队列优化dp,k次移动求最长路
洛谷2254 给你k次移动 每次移动给你一个时间段 a,b和方向dir 地图上有障碍物 为了不撞上障碍物你可以施法让箱子停下来 问箱子可以走的最长路 ((以下是洛谷的题解)) /*首先考虑对于时间t来 ...
- 洛谷 P1440 求m区间内的最小值(单调队列)
题目链接 https://www.luogu.org/problemnew/show/P1440 显然是一道单调队列题目…… 解题思路 对于单调队列不明白的请看这一篇博客:https://www.cn ...
- BZOJ_1800_[Ahoi2009]fly 飞行棋_乱搞
BZOJ_1800_[Ahoi2009]fly 飞行棋_乱搞 Description 给出圆周上的若干个点,已知点与点之间的弧长,其值均为正整数,并依圆周顺序排列. 请找出这些点中有没有可以围成矩形的 ...
- VIJOS1476 旅行规划(树形Dp + DFS暴力乱搞)
题意: 给出一个树,树上每一条边的边权为 1,求树上所有最长链的点集并. 细节: 可能存在多条最长链!最长链!最长链!重要的事情说三遍 分析: 方法round 1:暴力乱搞Q A Q,边权为正-> ...
- 【HDU6701】Make Rounddog Happy【权值线段树+双向单调队列】
题意:给你一个序列,求满足要求的子序列个数,其中要求为: 1.子序列的max-子序列长度len<=k 2.子序列中不出现重复的数字 题解:首先看到子序列max,很容易想到枚举最大值然后分治,这个 ...
- Codeforces 1182D Complete Mirror 树的重心乱搞 / 树的直径 / 拓扑排序
题意:给你一颗树,问这颗树是否存在一个根,使得对于任意两点,如果它们到根的距离相同,那么它们的度必须相等. 思路1:树的重心乱搞 根据样例发现,树的重心可能是答案,所以我们可以先判断一下树的重心可不可 ...
- BZOJ-3225 立方体覆盖 线段树+扫描线+乱搞
看数据范围像是个暴力,而且理论复杂度似乎可行,然后被卡了两个点...然后来了个乱搞的线段树+扫描线.. 3225: [Sdoi2008]立方体覆盖 Time Limit: 2 Sec Memory L ...
- “盛大游戏杯”第15届上海大学程序设计联赛夏季赛暨上海高校金马五校赛题解&&源码【A,水,B,水,C,水,D,快速幂,E,优先队列,F,暴力,G,贪心+排序,H,STL乱搞,I,尼姆博弈,J,差分dp,K,二分+排序,L,矩阵快速幂,M,线段树区间更新+Lazy思想,N,超级快速幂+扩展欧里几德,O,BFS】
黑白图像直方图 发布时间: 2017年7月9日 18:30 最后更新: 2017年7月10日 21:08 时间限制: 1000ms 内存限制: 128M 描述 在一个矩形的灰度图像上,每个 ...
随机推荐
- JQ中的 offsetTop 和 offset().top 的区别
话不多说先上图: offset()的top是指元素与document的上边的距离,而不是浏览器当前窗体的上边缘,如图 document高度超过window,浏览器出现滚动条,滚动滚动条,提交按钮的of ...
- 【blog】SpringBoot事务
参考链接 http://www.cnblogs.com/xingzc/p/6029483.html
- MGR架构 ~ 节点的维护相关问题
一简介:MGR节点的相关维护二 两种情况 1 MGR读节点异常停止,然后重新启动加入节点进行数据同步 2 MGR读节点新加入集群成员,启动复制进行数据同步三 通用过程 1 新加入节点通过通道 ...
- ps遇到的技术问题列表
1.ps矩形选框显示像素 CTRL+K 进入首选项设置就可以了. 2.ps显示辅助线 页面工具栏上的视图按钮,我们在列表上找到标尺,我们也是可以快捷键选择CtrI+R 3.如何将插入photoshop ...
- java jvm和android DVM区别
本文转自:http://blog.csdn.net/yujun411522/article/details/45932247 1.Android dvm的进程和Linux的进程, 应用程序的进 ...
- Crash工具实战-变量解析【转】
转自:http://blog.chinaunix.net/uid-14528823-id-4358785.html Crash工具实战-变量解析 Crash工具用于解析Vmcore文件,Vmcore文 ...
- css3实现不同进度条
进度条类型1(渐变进度条) 效果1:图片实现进度条 思路,进度条是一张图片,用定位来控制不同时间图片相对进度条box的left值来控制位置,用animate实现动画效果 html <div cl ...
- .NET之美 第一部分C#语言基础
第一章 类型基础 1 值类型与引用类型 CLR 支持两种类型:值类型和引用类型, C#的所有值类型均隐式派生自System.ValueType: 结构体:struct(直接派生于System.Valu ...
- Apollo 框架的剖析1
百度Apollo 自动驾驶开源模块分析 从今天开始研究学习apollo的源码,apollo 3.0源码. apollo 3.0的系统框图 文件目录简介 apollo根目录 ├── .github/IS ...
- HTML中添加音乐video embed audio
做H5页面时需要添加背景音乐,方法如下 方式一:<video controls="" autoplay="" name="media" ...