3784: 树上的路径

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 88  Solved: 27
[Submit][Status][Discuss]

Description

给定一个N个结点的树,结点用正整数1..N编号。每条边有一个正整数权值。用d(a,b)表示从结点a到结点b路边上经过边的权值。其中要求a<b.将这n*(n-1)/2个距离从大到小排序,输出前M个距离值。
 

Input

第一行两个正整数N,M
下面N-1行,每行三个正整数a,b,c(a,b<=N,C<=10000)。表示结点a到结点b有一条权值为c的边。
 

Output

共M行,如题所述.
 

Sample Input

5 10
1 2 1
1 3 2
2 4 3
2 5 4

Sample Output

7
7
6
5
4
4
3
3
2
1

HINT

N<=50000,M<=Min(300000,n*(n-1) /2 )

  通过参见hzwer的博客意识到这道题可以用类似于超级钢琴的做法。然后猛然意识到超级钢琴大概在半年前就A掉了,但是现在根本不会做。。。。经过深刻反思决定一定要把这个算法记住,不能再忘记了。

  这是一种询问第k大值问题的经典思路,考虑用对维护,我们可以发现如果堆中存所有的状态会mle,然而有些状态一定会在其他状态被取之后才可能被取,于是乎我们将这些“继承”状态的插入移到其母状态取到后再加入。

  然后我犯了一个非常经典的错误,看起来好像是没啥问题,堆也应该是线性的,但是对于大数据由于多数状态lca都不是x,极多的continue导致算法退化为n^2。以后要理解清楚有贡献状态和忽略但占用时间复杂度的状态规模大小的比例。

  正解为先点分,然后每一层点分维护dfs序与距中心距离,令四元组[x,l,r,lev]表示在lev层路径左端点为x,右端点为当前层dfs序中l到r中任意一个的最大值,每次提取最值并将区间分成两个。通过线段树维护区间最大值和位置。时间复杂度O(nlogn),空间复杂度O(nlogn),大概就是这样吧。。。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
using namespace std;
#define MAXN 51000
#define MAXV MAXN
#define MAXE MAXV*2
#define MAXT MAXN*40
#define INF 0x3f3f3f3f
#define smid ((l+r)>>1)
int n,m;
struct aaa
{
int x,l,r;
int lv;
pair<int,int> mx;
};
bool operator < (const aaa& a1,const aaa& a2)
{
return a1.mx<a2.mx;
}
struct sgt_node
{
int lc,rc;
pair<int,int> mx;
}sgt[MAXT];
int topt=;
void Build_sgt(int &now,int l,int r,int v[])
{
now=++topt;
if (l==r)
{
sgt[now].mx=make_pair(v[l],l);
return ;
}
Build_sgt(sgt[now].lc,l,smid,v);
Build_sgt(sgt[now].rc,smid+,r,v);
if (sgt[sgt[now].lc].mx>sgt[sgt[now].rc].mx)
sgt[now].mx=sgt[sgt[now].lc].mx;
else
sgt[now].mx=sgt[sgt[now].rc].mx;
}
pair<int,int> Query_sgt(int &now,int l,int r,int x,int y)
{
if (l==x && r==y)
return sgt[now].mx;
if (y<=smid)
return Query_sgt(sgt[now].lc,l,smid,x,y);
else if (smid<x)
return Query_sgt(sgt[now].rc,smid+,r,x,y);
else
return max(Query_sgt(sgt[now].lc,l,smid,x,smid),Query_sgt(sgt[now].rc,smid+,r,smid+,y)); } struct Edge
{
int np,val;
int id;
Edge *next;
}E[MAXE],*V[MAXV];
int tope=-;
void addedge(int x,int y,int z,int id)
{
E[++tope].np=y;
E[tope].val=z;
E[tope].id=id;
E[tope].next=V[x];
V[x]=&E[tope];
}
int elev[MAXE];
int pnt[MAXN];
int q[MAXN];
int siz[MAXN];
int get_core(int now)
{
Edge *ne;
int head=-,tail=;
q[]=now;
pnt[now]=now;
while (head<tail)
{
now=q[++head];
for (ne=V[now];ne;ne=ne->next)
{
if (ne->np==pnt[now] || ~elev[ne->id])continue;
q[++tail]=ne->np;
pnt[ne->np]=now;
}
}
for (register int i=tail;i>=;i--)
{
now=q[i];
siz[now]=;
for (ne=V[now];ne;ne=ne->next)
{
if (ne->np==pnt[now] || ~elev[ne->id])continue;
siz[now]+=siz[ne->np];
}
}
int ret;
int mxsiz=,bstsiz=INF;
if (siz[q[]]==)return -;
for (register int i=;i<=tail;i++)
{
now=q[i];
mxsiz=siz[q[]]-siz[now];
for (ne=V[now];ne;ne=ne->next)
{
if (ne->np==pnt[now] || ~elev[ne->id])continue;
mxsiz=max(mxsiz,siz[ne->np]);
}
if (bstsiz>mxsiz)
{
bstsiz=mxsiz;
ret=now;
}
}
return ret;
}
int seq[][MAXN];
int cdis[][MAXN];
int totsq[];
int stack[MAXN],tops=-;
void dfs1(int now,int dist,int lev,int p)
{
register Edge *ne;
seq[lev][totsq[lev]]=now;
cdis[lev][totsq[lev]]=dist;
totsq[lev]++;
stack[++tops]=now;
for (ne=V[now];ne;ne=ne->next)
{
if (~elev[ne->id] || ne->np==p)continue;
dfs1(ne->np,dist+ne->val,lev,now);
}
}
aaa stacka[MAXN*];
int topsa=-;
void solve(int now,int lev)
{
int core=get_core(now);
int tsiz=siz[q[]];
if (tsiz==)return ;
register Edge *ne;
for (ne=V[core];ne;ne=ne->next)
if (elev[ne->id]==-)
elev[ne->id]=lev;
int a,b;
aaa at;
at.lv=lev;
a=totsq[lev];
seq[lev][totsq[lev]]=core;
cdis[lev][totsq[lev]]=;
totsq[lev]++;
for (ne=V[core];ne;ne=ne->next)
{
if (elev[ne->id]!=lev)continue;
tops=-;
b=totsq[lev];
dfs1(ne->np,ne->val,lev,core);
for (register int i=b;i<totsq[lev];i++)
{
at.x=i;
at.l=a;
at.r=b-;
if (at.l<=at.r)stacka[++topsa]=at;
}
}
for (ne=V[core];ne;ne=ne->next)
{
if (elev[ne->id]!=lev)continue;
solve(ne->np,lev+);
}
}
int root[];
aaa heap[MAXN*];
int toth=;
int main()
{
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
int x,y,z;
scanf("%d%d",&n,&m);
for (int i=;i<n;i++)
{
scanf("%d%d%d",&x,&y,&z);
addedge(x,y,z,i);
addedge(y,x,z,i);
}
memset(elev,-,sizeof(elev));
solve(,);
for (int i=;i<;i++)
if (totsq[i])
Build_sgt(root[i],,totsq[i]-,cdis[i]); pair<int,int> pr;
while (~topsa)
{
x=cdis[stacka[topsa].lv][stacka[topsa].x];
stacka[topsa].mx=Query_sgt(root[stacka[topsa].lv],,totsq[stacka[topsa].lv]-,stacka[topsa].l,stacka[topsa].r);
stacka[topsa].mx.first+=x;
heap[toth]=stacka[topsa--];
push_heap(heap,heap+(++toth));
}
int rnow=;
aaa at,at2;
while (rnow<m)
{
at=heap[];
pop_heap(heap,heap+(toth--));
rnow++;
printf("%d\n",at.mx.first);
at2=at;
at2.r=at2.mx.second-;
if (at2.l<=at2.r)
{
x=cdis[at2.lv][at2.x];
at2.mx=Query_sgt(root[at2.lv],,totsq[at2.lv]-,at2.l,at2.r);
at2.mx.first+=x;
heap[toth]=at2;
push_heap(heap,heap+(++toth));
}
at2=at;
at2.l=at2.mx.second+;
if (at2.l<=at2.r)
{
x=cdis[at2.lv][at2.x];
at2.mx=Query_sgt(root[at2.lv],,totsq[at2.lv]-,at2.l,at2.r);
at2.mx.first+=x;
heap[toth]=at2;
push_heap(heap,heap+(++toth));
}
}
}

bzoj 3784: 树上的路径 堆维护第k大的更多相关文章

  1. BZOJ 3784: 树上的路径

    Description 问一棵树上前 \(k\) 大路径的边权. Sol 边分治. 非常感谢数据没有菊花图. 为了写写边分治试试然后就开了这道题. 边分治非常好想,选一条重边,分成两部分,然后分别求最 ...

  2. BZOJ.3784.树上的路径(点分治 贪心 堆)

    BZOJ \(Description\) 给定一棵\(n\)个点的带权树,求树上\(\frac{n\times(n-1)}{2}\)条路径中,长度最大的\(m\)条路径的长度. \(n\leq5000 ...

  3. bzoj 3784: 树上的路径【点分治+st表+堆】

    参考:https://www.cnblogs.com/CQzhangyu/p/7071477.html 神奇的点分治序(或者叫点剖?).就是把点分治扫过的点依次放进队列里,然后发现,对于每一棵树摊到序 ...

  4. BZOJ 3784: 树上的路径 点分治+二分+set

    很容易想出二分这个思路,但是要想办法去掉一个 $log$. 没错,空间换时间. 双指针的部分错了好几次~ Code: #include <set> #include <queue&g ...

  5. LCA+主席树 (求树上路径点权第k大)

      SPOJ 10628. Count on a tree (树上第k大,LCA+主席树) 10628. Count on a tree Problem code: COT You are given ...

  6. BZOJ2006:超级钢琴(ST表+堆求前K大区间和)

    Description 小Z是一个小有名气的钢琴家,最近C博士送给了小Z一架超级钢琴,小Z希望能够用这架钢琴创作出世界上最美妙的音乐. 这架超级钢琴可以弹奏出n个音符,编号为1至n.第i个音符的美妙度 ...

  7. bzoj 1528 [POI2005]sam-Toy Cars 堆维护+贪心

    1528: [POI2005]sam-Toy Cars Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 716  Solved: 306[Submit][S ...

  8. 【BZOJ-3784】树上的路径 点分治 + ST + 堆

    3784: 树上的路径 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 462  Solved: 153[Submit][Status][Discuss ...

  9. bzoj4165 矩阵 堆维护多路归并

    题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4165 题解 大概多路归并是最很重要的知识点了吧,近几年考察也挺多的(虽然都是作为签到题的). ...

随机推荐

  1. C#自动实现的属性

    using System; using System.Collections.Generic; using System.Text; namespace 自动属性 { class Program { ...

  2. Android 自定义View修炼-实现自定义圆形、圆角和椭圆ImageView(使用Xfermode图形渲染方法)

    一:简介: 在上一篇<Android实现圆形.圆角和椭圆自定义图片View(使用BitmapShader图形渲染方法)>博文中,采用BitmapShader方法实现自定义的圆形.圆角等自定 ...

  3. 基于anyrtc的sdk实现直播连麦互动

    基于anyrtc的sdk实现直播连麦互动 前言 1.由于粘贴了较大的代码,造成内容比较长,可能会花费您较长的时间. 2.项目里面没有做权限判断,所以如果发现有页面发生崩溃可能是权限没有打开,请打开权限 ...

  4. javabean对象要实现的接口们和要重写的方法们

    在使用list集合的时候,什么也不用. 原因:list允许存储重复的元素. 在使用set集合的时候,要重写,equals()方法 和 hashCode() 方法. 愿意:set集合 不允许存放相同的元 ...

  5. 深入理解计算机系统第二版习题解答CSAPP 2.3

    填写空白.单字节可以用两个十六进制数表示. 十进制 二进制 十六进制 0 0000 0000 0x00 167 1010 0111 0xA7 62 0011 1110 0x3E 188 1011 11 ...

  6. "ORA-00054: 资源正忙, 但指定以 NOWAIT 方式获取资源, 或者超时失效"的快速解决方法

    引自:http://hi.baidu.com/fynaa/item/c2978952d8d542dfd48bacf6 讲了一大堆: 综合下: 解决方案:select session_id from v ...

  7. LSJ_NHibernate第三章 IDAL,DAL,BLL

    前言: 做项目(面向数据编程),首先必须了解业务,这是核心,不懂业务写出来的代码毫无意义.业务我这里分为两种,简单业务操作,复杂业务操作,我以他们操作表的界限进行区分,假设我更新一条数据,只操作了一张 ...

  8. MFC对话框程序EDIT类控件的自动换行,垂直滚动条自动下移

    1.新建一个Edit Control,将其Multiline属性设置为True,Auto HScroll属性设置False,这样就可以实现每一行填满后自动换行了.   2.再将Vetrical Scr ...

  9. c语言学习之基础知识点介绍(十九):内存操作函数

    一.malloc函数 /* 首先需要导入头文件 #include <stdlib.h> malloc void* malloc(n); n是字节大小 开辟堆空间,开辟的字节数以n为准 返回 ...

  10. Java_Iterator-------迭代器配合Listarray使用,具有更多的功能(转载)

    转载自:http://www.cnblogs.com/amboyna/archive/2007/09/25/904804.html 迭代器(Iterator) 迭代器是一种设计模式,它是一个对象,它可 ...