直径上的乱搞一般要求出这条直径上的点集或者边集

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负权树求直径+求直径边的更多相关文章

  1. 完美字符子串 单调队列预处理+DP线段树优化

    题意:有一个长度为n的字符串,每一位只会是p或j.你需要取出一个子串S(注意不是子序列),使得该子串不管是从左往右还是从右往左取,都保证每时每刻已取出的p的个数不小于j的个数.如果你的子串是最长的,那 ...

  2. 单调队列优化dp,k次移动求最长路

    洛谷2254 给你k次移动 每次移动给你一个时间段 a,b和方向dir 地图上有障碍物 为了不撞上障碍物你可以施法让箱子停下来 问箱子可以走的最长路 ((以下是洛谷的题解)) /*首先考虑对于时间t来 ...

  3. 洛谷 P1440 求m区间内的最小值(单调队列)

    题目链接 https://www.luogu.org/problemnew/show/P1440 显然是一道单调队列题目…… 解题思路 对于单调队列不明白的请看这一篇博客:https://www.cn ...

  4. BZOJ_1800_[Ahoi2009]fly 飞行棋_乱搞

    BZOJ_1800_[Ahoi2009]fly 飞行棋_乱搞 Description 给出圆周上的若干个点,已知点与点之间的弧长,其值均为正整数,并依圆周顺序排列. 请找出这些点中有没有可以围成矩形的 ...

  5. VIJOS1476 旅行规划(树形Dp + DFS暴力乱搞)

    题意: 给出一个树,树上每一条边的边权为 1,求树上所有最长链的点集并. 细节: 可能存在多条最长链!最长链!最长链!重要的事情说三遍 分析: 方法round 1:暴力乱搞Q A Q,边权为正-> ...

  6. 【HDU6701】Make Rounddog Happy【权值线段树+双向单调队列】

    题意:给你一个序列,求满足要求的子序列个数,其中要求为: 1.子序列的max-子序列长度len<=k 2.子序列中不出现重复的数字 题解:首先看到子序列max,很容易想到枚举最大值然后分治,这个 ...

  7. Codeforces 1182D Complete Mirror 树的重心乱搞 / 树的直径 / 拓扑排序

    题意:给你一颗树,问这颗树是否存在一个根,使得对于任意两点,如果它们到根的距离相同,那么它们的度必须相等. 思路1:树的重心乱搞 根据样例发现,树的重心可能是答案,所以我们可以先判断一下树的重心可不可 ...

  8. BZOJ-3225 立方体覆盖 线段树+扫描线+乱搞

    看数据范围像是个暴力,而且理论复杂度似乎可行,然后被卡了两个点...然后来了个乱搞的线段树+扫描线.. 3225: [Sdoi2008]立方体覆盖 Time Limit: 2 Sec Memory L ...

  9. “盛大游戏杯”第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 描述 在一个矩形的灰度图像上,每个 ...

随机推荐

  1. linux 开发板上的调试

    1.需要命令 ulimit 进行设置core file size ,  看 core file size. cat /proc/pid/limits,  这个暂时不用 2.需要有gdb命令 , 需要g ...

  2. 数组去重的4种方法(Which one is the fastest???嘻嘻嘻....)

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  3. DeepLearning.ai-Week1-Convolution+model+-+Application

    1.0 - TensorFlow model 导入相关依赖包. import numpy as np import h5py import matplotlib.pyplot as plt impor ...

  4. wx小程序-音频视频!

    1.音乐的启动跟暂停 dom里面图片切换的另一种方法 通过变量 改变路径 2.监听 在onload里面 3.定义了一个全局变量 然后在但页面中获取 app.js 单页面中 app.js 的三个生命周期

  5. Javascript - ExtJs - 数据

    数据(ExtJs Data) Ext.data命名空间 有关数据存储.读取的类都定义在Ext.data命名空间中.Ext的gridPanel.combobox的数据源都是来自Ext.data提供的类. ...

  6. 20165237 2017-2018-2 《Java程序设计》第9周学习总结

    20165237 2017-2018-2 <Java程序设计>第9周学习总结 教材学习内容总结 1.URL类是java.net包中的一个重要的类,URL的实例封装着一个统一资源定位符(Un ...

  7. C&C++动态分配内存(手动分配内存)三种方式

    1. malloc函数 函数原型:void *malloc(unsigned int size)函数的作用是:在内训的动态存储区开辟一个size个字节的连续空间,返回所分配区域的首字节地址. 可以看到 ...

  8. oracle监听的动态注册和静态注册

    参考资料: https://blog.csdn.net/tianlesoftware/article/details/5543166 https://www.cnblogs.com/guilingya ...

  9. (转)如何用TensorLayer做目标检测的数据增强

    数据增强在机器学习中的作用不言而喻.和图片分类的数据增强不同,训练目标检测模型的数据增强在对图像做处理时,还需要对图片中每个目标的坐标做相应的处理.此外,位移.裁剪等操作还有可能使得一些目标在处理后只 ...

  10. WYSIWYG WebBuilder 所见即所得工具

    WYSIWYG WebBuilder  所见即所得工具: http://www.wysiwygwebbuilder.com/