直径上的乱搞 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 描述 在一个矩形的灰度图像上,每个 ...
随机推荐
- Leetcode#169. Majority Element(求众数)
题目描述 给定一个大小为 n 的数组,找到其中的众数.众数是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素. 你可以假设数组是非空的,并且给定的数组总是存在众数. 示例 1: 输入: [3,2,3] ...
- BigDecimal实现末尾去掉无用0
BigDecimal 原生提供了 stripTrailingZeros 方法可以实现去掉末尾的 0,然后使用 toPlainString 可以输出数值,注意这里如果使用 toString() 会变成 ...
- Django REST framework 第三章 CBV
从介绍Django快开始,我们就一直在使用FBV的方式来撰写代码,二者本质上并没有太大的区别,然而到了REST framework,更会倾向于用CBV来写API的视图,后面会看到这个方式的强大,它允许 ...
- 【blog】Markdown的css样式推荐
参考博客 分享一款Markdown的css样式:https://www.cnblogs.com/zhangjk1993/p/5442676.html 美化Markdown输出的HTML文档:http: ...
- 在IDEA中新建Spring Boot项目
新建项目 选择项目构建方式 选择项目依赖 新建项目成功后(Maven构建方式)
- python 中的"*"与"**"
1.Python中 *和**很常见的运算符的用途及其多种使用方式. 本文所述的*和**,指的是*和**前缀运算符,而不是中缀运算符. 所以指的不是乘法和乘幂: 使用*和**将参数传递给函数 使用*和* ...
- mysql案例~mysql主从复制延迟概总
浅谈mysql主从复制延迟 1 概念解读 需要知道以下几点 1 mysql的主从同步上是异步复制,从库是串行化执行 2 mysql 5.7的并行复制能加速从库重做的速度,进一步缓解 主从同步的延迟问题 ...
- Nginx在局域网中使用ip_hash负载均衡策略,访问全部分发到同一个后台服务器
Nginx的ip_hash算法都将一个ip地址的前三段作为hash的关键字
- About me & OI这一年
1 最近碰到一些 OIers 问我学 OI 的经历 回忆了一下,我做的第一道题是A+B Problem,时间:2018年2月15日 呀,正好一年了 2 我是来自 HB 的 OIer 一枚 现在高一,就 ...
- ios webview调试
chrome://inspect/#devices if (Build.VERSION.SDK_INT >=Build.VERSION_CODES.KITKAT) { WebView.setWe ...