BZOJ.3784.树上的路径(点分治 贪心 堆)
\(Description\)
给定一棵\(n\)个点的带权树,求树上\(\frac{n\times(n-1)}{2}\)条路径中,长度最大的\(m\)条路径的长度。
\(n\leq50000,\ m\leq\min(3\times10^5,\frac{n\times(n-1)}{2})\)。
\(Solution\)
利用 点分治可以处理出树上所有路径 的性质,在每次点分治处理子树时,我们把当前根\(root\)和访问到的点\(x\)依次存到同一个数组里,把存下来的\(dis(x,root)\)序列记作\(d_i\)。
再由点分治的性质,这样得到的数组长度是\(O(n\log n)\)的。
\(x\)与\(root\)以及\(root\)之前子树中的点\(y\)可以形成一条路径,而且这些点(\(root\)和\(y\))现在在数组中的位置是一段连续的区间,设为\([l,r]\)。
那么\(dis(x,root)\)能和\(d_l,d_{l+1},...,d_r\)中的任意一个数组合,得到一条路径长为\(dis(x,root)+d_i\)。
那么问题就变成了,对于一个长为\(n\log n\)的序列中的每个数\(i\),它可以和某个区间\([l_i,r_i]\)中的数\(j\)组合,得到长度为\(d_i+d_j\)的路径。我们要选出最长的\(m\)条。
怎么做呢。
最初每个数\(i\)肯定是和\([l_i,r_i]\)中\(d_j\)最大的\(j\)组合(RMQ预处理),也就是第一大的值肯定是从所有\(i\)与\([l_i,r_i]\)最大的\(j_i\)中组合,然后选最大的。
假设我们选出\(k\)(\(k\)和对应的\(j_k\)是所有\(i\)中\(d_i+d_j\)最大的),然后之后\(k\)只能和\([l_k,j)\bigcup(j,r_k]\)这些数组合。
而第二大的值要么是从其它的那些\((i,j_i)\)中选,要么再拿\(k\)和\([l_k,j)\bigcup(j,r_k]\)中的数组合。
所有我们用堆得到\(k\)后,再拿\(k\)分别和\([l_k,j)\)中最大的数组合、\((j,r_k]\)中最大的数组合,再扔到堆里就好了。
复杂度\(O((n\log n+m)\log(n\log n))\)。
ps:在其他题里\(n\)还没有那么大,所以ST表的一二维顺序影响不大。呸 (起码在BZOJ上)影响很大。
但是在这题差别就更明显了。。(7252ms$\to$2896ms)
这个贪心还可以用可持久化可并堆写。。
//103684kb 2836ms
#include <queue>
#include <cstdio>
#include <cctype>
#include <algorithm>
#define gc() getchar()
#define MAXIN 300000
//#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=50005,M=N*16;
int Enum,H[N],nxt[N<<1],to[N<<1],len[N<<1],Min,root,sz[N],tot,d[M],L[M],R[M],Ln,Rn,Log[M],pos[20][M];
bool vis[N];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Node
{
int val,di,l,r;
bool operator <(const Node &a)const
{
return val<a.val;
}
};
std::priority_queue<Node> q;
inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-48,c=gc());
return now;
}
inline void AE(int w,int u,int v)
{
to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum, len[Enum]=w;
to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum, len[Enum]=w;
}
inline int Max(int x,int y)
{
return d[x]>d[y]?x:y;
}
inline int Query(int l,int r)
{
int k=Log[r-l+1];
return Max(pos[k][l],pos[k][r-(1<<k)+1]);
}
void FindRoot(int x,int fa,int tot)
{
int mx=0; sz[x]=1;
for(int i=H[x],v; i; i=nxt[i])
if(!vis[v=to[i]] && v!=fa)
FindRoot(v,x,tot), sz[x]+=sz[v], sz[v]>mx&&(mx=sz[v]);
mx=std::max(mx,tot-sz[x]);
if(mx<Min) Min=mx, root=x;
}
void Calc(int x,int fa,int dep)
{
d[++tot]=dep, L[tot]=Ln, R[tot]=Rn;
for(int i=H[x],v; i; i=nxt[i])
if(!vis[v=to[i]] && v!=fa) Calc(v,x,dep+len[i]);
}
void Solve(int x)
{
vis[x]=1, d[++tot]=0, L[tot]=1, R[tot]=0, Ln=Rn=tot;
for(int i=H[x]; i; i=nxt[i])
if(!vis[to[i]]) Calc(to[i],x,len[i]), Rn=tot;
for(int i=H[x]; i; i=nxt[i])
if(!vis[to[i]]) Min=N, FindRoot(to[i],x,sz[to[i]]), Solve(root);
}
int main()
{
const int n=read(),m=read();
for(int i=1; i<n; ++i) AE(read(),read(),read());
Min=N, FindRoot(1,1,n), Solve(root);
pos[0][1]=1;
for(int i=2; i<=tot; ++i) Log[i]=Log[i>>1]+1, pos[0][i]=i;
for(int j=1; j<=Log[tot]; ++j)//写成 j<=Log[n],i=n-t,还能过除了第6个点外的所有点= =。
for(int t=1<<j-1,i=tot-t; i; --i)
pos[j][i]=Max(pos[j-1][i],pos[j-1][i+t]);
for(int i=1; i<=tot; ++i)
if(L[i]<=R[i]) q.push((Node){d[i]+d[Query(L[i],R[i])],d[i],L[i],R[i]});
for(int i=1,p; i<=m; ++i)
{
Node tmp=q.top(); q.pop();
printf("%d\n",tmp.val), p=Query(tmp.l,tmp.r);
if(tmp.l<p) q.push((Node){tmp.di+d[Query(tmp.l,p-1)],tmp.di,tmp.l,p-1});
if(p<tmp.r) q.push((Node){tmp.di+d[Query(p+1,tmp.r)],tmp.di,p+1,tmp.r});
}
return 0;
}
BZOJ.3784.树上的路径(点分治 贪心 堆)的更多相关文章
- BZOJ 3784: 树上的路径 点分治+二分+set
很容易想出二分这个思路,但是要想办法去掉一个 $log$. 没错,空间换时间. 双指针的部分错了好几次~ Code: #include <set> #include <queue&g ...
- bzoj 3784: 树上的路径 堆维护第k大
3784: 树上的路径 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 88 Solved: 27[Submit][Status][Discuss] ...
- 【BZOJ-3784】树上的路径 点分治 + ST + 堆
3784: 树上的路径 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 462 Solved: 153[Submit][Status][Discuss ...
- bzoj 3784: 树上的路径【点分治+st表+堆】
参考:https://www.cnblogs.com/CQzhangyu/p/7071477.html 神奇的点分治序(或者叫点剖?).就是把点分治扫过的点依次放进队列里,然后发现,对于每一棵树摊到序 ...
- BZOJ 3784: 树上的路径
Description 问一棵树上前 \(k\) 大路径的边权. Sol 边分治. 非常感谢数据没有菊花图. 为了写写边分治试试然后就开了这道题. 边分治非常好想,选一条重边,分成两部分,然后分别求最 ...
- 【BZOJ3784】树上的路径 点分治序+ST表
[BZOJ3784]树上的路径 Description 给定一个N个结点的树,结点用正整数1..N编号.每条边有一个正整数权值.用d(a,b)表示从结点a到结点b路边上经过边的权值.其中要求a< ...
- BZOJ 1316: 树上的询问( 点分治 + 平衡树 )
直接点分治, 用平衡树(set就行了...)维护. -------------------------------------------------------------------------- ...
- BZOJ 3697: 采药人的路径 [点分治] [我想上化学课]
传送门 题意: 路径有$-1,1$两种权值,求有多少路径满足权值和为$0$且有一个点将路径分成权值和为$0$的两段 第四节课本来想去上化学,然后快上课了这道题还没调出来.....可恶我想上化学 昨天两 ...
- bzoj 1907: 树的路径覆盖【贪心+树形dp】
我是在在做网络流最小路径覆盖的时候找到这道题的 然后发现是个贪心+树形dp \( f[i] \)表示在\( i \)为根的子树中最少有几条链,\( v[i] \) 表示在\( i \)为根的子树中\( ...
随机推荐
- lightoj 1282 取对数的操作
/* 前三位 len=log10n^k(乘积的长度) len=klog10n n^k=x*10^(len-1) x=n^k/10^(len-1) log10x = k*log10n - (len-1) ...
- Nginx详解二十三:Nginx深度学习篇之Nginx+Lua开发环境搭建
Nginx+Lua开发环境 1.下载LuaJIT解释器wget http://luajit.org/download/LuaJIT-2.0.2.tar.gztar -zxvf LuaJIT-2.0.2 ...
- OrCAD Capture CIS 16.6 导出BOM
OrCAD Capture CIS 16.6 一.选择设计文件:菜单:Tools > Bill of Materials... 二.Bill of Materials > Open in ...
- vsftpd中的local_umask和anon_umask
umask是在linux中常见的一个东西,它其实是一个掩码.当然,也有umask这样一个命令,它是对用户建立的文件的默认属性的定义.该 定义为: 假设umask为022,则对于一个文件夹的话,它的默认 ...
- 论文阅读笔记三十三:Feature Pyramid Networks for Object Detection(FPN CVPR 2017)
论文源址:https://arxiv.org/abs/1612.03144 代码:https://github.com/jwyang/fpn.pytorch 摘要 特征金字塔是用于不同尺寸目标检测中的 ...
- Nancy 返回值详解
简介 Nancy 是一个轻量级的,简单粗暴的framework用来构建基于HTTP的各种服务,兼容.Net和Mono.它的返回值也是多种多样的,适应各种不同的情况.包括Response.AsFile( ...
- 作为完美主义者(强迫症)如何将linux的eth1网卡修改为eth0网卡
1:由于你是克隆的虚拟机或者直接就是使用别人的Centos操作系统直接打开到自己本地的虚拟机,网卡可能就是eth1而不是eth0,下面简单写一下如何将eth1修改为eth0,步骤如下所示: 2:如果你 ...
- Quartz.net入门
简介 Quartz.NET是一个开源的作业调度框架,是OpenSymphony的 Quartz API的.NET移植,它用C#写成,可用于winform和asp.net应用中.它提供了巨大的灵活性而不 ...
- JS获取地址栏的参数值
function GetQueryString(name){ var reg = new RegExp("(^|&)"+ name +"=([^&]*)( ...
- 【BZOJ1417】Pku3156 Interconnect
题解: 比较简单的一道题 显然我们只需要知道每个联通块的大小就可以了 然后发现x1+xn=30 其中xi<=xi+1的状态只有5000 所以直接记忆化搜索就可以了 emm原来map还可以映射ve ...