首页 > 其他分享 >BZOJ #3784. 树上的路径

BZOJ #3784. 树上的路径

时间:2023-07-14 20:35:24浏览次数:42  
标签:sz posj val 3784 int 路径 lca BZOJ define

BZOJ #3784. 树上的路径

题意

给一颗树,求所有路径长度中前 \(k\) 大。

题解

首先对于前 \(k\) 大,我们有一个常见的方法,二分。
二分第 \(k\) 大的路径长度,然后使用点分治统计,点分治内部还要二分,所以时间复杂度 \(O(nolg^3n)\) 。
二分显然是行不通了,想一下就会发现外层和内层的二分都不好去掉。
我们考虑求前 \(k\) 小要怎么做,显然可以将所有的边丢到优先队列中,然后取出队头,扩展然后又丢回去。
但是由于总方案数太多,我们无法通过取小的将大的推出来,所以我们考虑修改一下这个做法,超级钢琴这道题就是一个好方法。
但是扩展到树上不太方便,我们考虑一个和前缀和类似的方法。
已知 \(dis_{x,y} = dis_{x,lca}+dis_{lca,y}\) ,然后我们可以枚举 \(x\) ,和 \(lca\),然后再通过 \(dfn\)转化为区间,记录 \(x,lca\) 拆两个元组然后丢进去。
注意到枚举 \(lca\) 这个东西是和深度有关的,所以我们可以使用点分治来枚举 \(lca\) 。
然后对于每一个分治点,将其子树全部记录下来,用类似 \(dfn\) 的方式,然后我们会发现每一个 \(x\),对应的都是一个区间。
然后就和超级钢琴一模一样了,记录点数是 \(nlogn\) 级别的,然后算上优先队列,时间复杂度 \(O(nlog^2n)\)。

code

点击查看代码
#include <bits/stdc++.h>
#define rg register
#define pc putchar
#define gc getchar
#define pf printf
#define space pc(' ')
#define enter pc('\n')
#define me(x,y) memset(x,y,sizeof(x))
#define pb push_back
#define FOR(i,k,t,p) for(rg int i(k) ; i <= t ; i += p)
#define ROF(i,k,t,p) for(rg int i(k) ; i >= t ; i -= p)
using namespace std ;
inline void read(){}
template<typename T,typename ...T_>
inline void read(T &x,T_&...p)
{
    x = 0 ;rg int f(0) ; rg char c(gc()) ;
    while(!isdigit(c)) f |= (c=='-'),c = gc() ;
    while(isdigit(c)) x = (x<<1)+(x<<3)+(c^48),c = gc() ;
    x = (f?-x:x) ;
    read(p...);
}
int stk[30],tp ;
inline void print(){}
template<typename T,typename ...T_>
inline void print(T x,T_...p)
{
    if(x < 0) pc('-'),x = -x ;
    do stk[++tp] = x%10,x /= 10 ; while(x) ;
    while(tp) pc(stk[tp--]^48) ; space ;
    print(p...) ;
}
const int N = 1e6+5 ;
int n,m,sum,top,le,ri,rt ;
int L[N],R[N] ;
int sz[N],vis[N],a[N],mx[N][30],val[N] ;
struct Edge{int v,w ;} ;
vector<Edge>po[N],e[N] ;
void Get_sz(int x,int fa)
{
    sz[x] = 1 ;
    for(auto [v,w]:e[x]) if(!vis[v] && v != fa)
        Get_sz(v,x),sz[x] += sz[v] ;
}
void Get_rt(int x,int fa)
{
    sz[x] = 1 ; int mix = 0 ;
    for(auto [v,w]:e[x]) if(!vis[v] && v != fa)
        Get_rt(v,x),sz[x] += sz[v],mix = max(sz[v],mix) ;
    mix = max(mix,sum-sz[x]) ;
    if(mix <= sum/2) rt = x ;    
}
void Get(int x,int fa,int dis)
{
    val[++top] = dis,L[top] = le,R[top] = ri ;
    for(auto [v,w]:e[x]) if(!vis[v] && v != fa) Get(v,x,dis+w) ;
}
void Solve(int x)
{
    vis[x] = 1,le = ++top ;
    for(auto [v,w]:e[x]) if(!vis[v])
        ri = top,Get(v,x,w) ;
    for(auto [v,w]:e[x]) if(!vis[v])
        rt = 0,Get_sz(v,x),sum = sz[v],Get_rt(v,x),Solve(rt) ;
}
inline int Max(int x,int y){return val[x]>val[y]?x:y ;}
struct Node
{
    int l,r,posi,posj ;
    bool operator < (const Node &A) const
    {
        return val[posi]+val[posj] < val[A.posi]+val[A.posj] ;
    }
} ;
priority_queue<Node>q ;
inline int Query(int l,int r)
{
    // print(l,r),enter ;
    int k = log2(r-l+1) ;
    return Max(mx[l][k],mx[r-(1<<k)+1][k]) ;
}
signed main()
{
//	freopen(".in","r",stdin) ;
//	freopen(".out","w",stdout) ;
    read(n,m) ;
    FOR(i,2,n,1)
    {
        int u,v,w ; read(u,v,w) ;
        e[u].pb({v,w}),e[v].pb({u,w}) ;
    }
    sz[rt = 0] = n+1,sum = n,Get_rt(1,0),Solve(rt) ;
    FOR(i,1,top,1) mx[i][0] = i ;
    FOR(j,1,20,1) for(int i = 1 ; i+(1<<j)-1 <= top ; ++i)
        mx[i][j] = Max(mx[i][j-1],mx[i+(1<<j-1)][j-1]) ;
    FOR(i,1,top,1) if(L[i]) q.push({L[i],R[i],i,Query(L[i],R[i])}) ;
    // FOR(i,1,top,1) print(val[i]) ; print(top),cerr<<"!",enter ;
    // FOR(i,1,top,1) if(L[i]) print(L[i],R[i],i,Query(L[i],R[i])),enter ;
    // FOR(i,1,top,1) print(L[i],R[i]),enter ;
    FOR(i,1,m,1)
    {
        auto [l,r,posi,posj] = q.top() ; q.pop() ;
        print(val[posi]+val[posj]),enter ;
        if(posj > l) q.push({l,posj-1,posi,Query(l,posj-1)}) ;
        if(posj < r) q.push({posj+1,r,posi,Query(posj+1,r)}) ;
    }
    return 0 ;
}

...

求前 \(k\) 大,这种类型的题,贪心和二分都是可以使用的技巧,但是要注意数据规模。
看到统计路径长度我们可以往中转点和点分治的方向去想。

标签:sz,posj,val,3784,int,路径,lca,BZOJ,define
From: https://www.cnblogs.com/snow-panther/p/17554904.html

相关文章

  • /login接口路径404但是拦截器却显示路径为/error
    参考文献:springboot全局异常处理中的404的/error重复拦截问题(https://blog.csdn.net/qq_35890572/article/details/106529428)问题:loginInterceptor在经过后,目标接口/login报错,又进入拦截器了,但是断点显示路径为/error因为在接口异常后,SpringMVC会去寻找有没有对应异常的统一处理......
  • 如何让虚拟机共享主机路径一致的映射文件
    首先前提是需要在安装了win10系统的虚拟机,包括安装了tools工具。以及一台win10主机。详细安装步骤参考我的另一篇文章:如何在win10系统主机中安装win10系统虚拟机(附win10镜像和VMwareStation15Pro安装包)-IT知识生产小店铺-博客园(cnblogs.com) 必须知道一个大前提,主机......
  • pip show 显示模块插件包安装路径、信息
    显示某个模块(包、插件)安装路径、版本信息pipshowFlask或pip3showFlask效果:参考:https://www.zhihu.com/question/603263580?utm_id=0......
  • python 获取加载模块路径
    方法一:python3-c"importsys;print(sys.path)"效果:方法二:python3importsysprint(sys.path)效果:参考:https://www.zhihu.com/question/603263580?utm_id=0......
  • java导出的excel默认路径
    如何设置Java导出Excel的默认路径作为一名经验丰富的开发者,我将指导你如何实现Java导出Excel的默认路径。下面是整个流程的步骤:步骤操作1创建一个Excel文件对象2设置Excel文件的默认导出路径3创建一个Sheet对象4向Sheet中添加数据5保存Excel文件现......
  • dede开启网站绝对路径后软件下载地址出错
    今天教大家如何解决(织梦CMS启用绝对网址后,下载页面的软件下载地址出错)织梦开启绝对路径后软件模型,下载地址填https://开头的,调用出来会显示 域名+https://网址,如下图网址解决方法:1、打开/plus/download.php找到大概在147行if(!preg_match("#^http://|^thunder://|^ftp://|......
  • Vscode 设置别名路径和创建快捷模板
    设置别名路径创建jsconfig.json文件,配置@文件路径{"compilerOptions":{"baseUrl":"./","paths":{"@/*":["src/*"]}}} 创建快捷模板 文件->首选项->配置用户代码片段 新建全局代码片段文件......
  • 怎么查看java安装路径 这个问题怎么解决?
    如何查看Java安装路径在开发Java应用程序时,我们经常需要查看Java的安装路径,以便配置环境变量、设置Java路径等操作。本文将介绍几种查看Java安装路径的方法,并提供相应的代码示例。方法一:使用Java命令Java提供了命令行工具java和javac,我们可以通过执行java-version命令来查看Jav......
  • 图的应用---关键路径
    图的应用---关键路径关键路径需完成的活动,活动所需要的时间,以先期需要完成工作例1例2把工程计划表示为边表示活动的网络,即AOE网,用顶点表示事件,弧表示活动,弧的权表示活动持续的时间.事件表示在它之前的活动已经完成,在它之后的活动可以开始.可以抽象成一个网络......
  • 力扣---931. 下降路径最小和
    给你一个 nxn 的 方形 整数数组 matrix ,请你找出并返回通过 matrix 的下降路径 的 最小和 。下降路径 可以从第一行中的任何元素开始,并从每一行中选择一个元素。在下一行选择的元素和当前行所选元素最多相隔一列(即位于正下方或者沿对角线向左或者向右的第一个元素)......