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

3784: 树上的路径

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 462  Solved: 153
[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 

Source

Solution

超级钢琴推广到树上版本

利用点分治每个点的时间戳,将树转化到序列上,顺带记录每个节点,能和他组成一条路径的左右端点L,R

然后利用超级钢琴的方法去处理就好,具体见这里

Code

#include<iostream> 
#include<cstdio> 
#include<cstring> 
#include<algorithm> 
#include<queue> 
using namespace std; 
int read() 
{ 
    int x=0,f=1; char ch=getchar(); 
    while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();} 
    while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();} 
    return x*f; 
} 
#define MAXN 100010 
#define MAXM 1000100 
int N,M; 
struct EdgeNode{int next,to,val;}edge[MAXN<<1]; 
int head[MAXN],cnt=1; 
void AddEdge(int u,int v,int w) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v; edge[cnt].val=w;} 
void InsertEdge(int u,int v,int w) {AddEdge(u,v,w); AddEdge(v,u,w);} 
int maxx[MAXN],size[MAXN],Sz,root,L[MAXM],R[MAXM],D[MAXM],pl,pr,dfn; 
bool visit[MAXN]; 
void DFSRoot(int now,int last) 
{ 
    size[now]=1; maxx[now]=0; 
    for (int i=head[now]; i; i=edge[i].next) 
        if (edge[i].to!=last && !visit[edge[i].to]) 
            { 
                DFSRoot(edge[i].to,now); 
                size[now]+=size[edge[i].to]; 
                maxx[now]=max(maxx[now],size[edge[i].to]); 
            } 
    maxx[now]=max(maxx[now],Sz-size[now]); 
    if (maxx[now]<maxx[root]) root=now; 
} 
void Get(int x,int last,int Dis) 
{ 
    D[++dfn]=Dis; L[dfn]=pl,R[dfn]=pr; 
    for (int i=head[x]; i; i=edge[i].next) 
        if (!visit[edge[i].to] && edge[i].to!=last) 
            Get(edge[i].to,x,Dis+edge[i].val); 
} 
void Divide(int x) 
{ 
    visit[x]=1; 
    pl=pr=++dfn; 
    for (int i=head[x]; i; i=edge[i].next) 
        if (!visit[edge[i].to]) Get(edge[i].to,x,edge[i].val),pr=dfn; 
    for (int i=head[x]; i; i=edge[i].next) 
        if (!visit[edge[i].to])  
            { 
                Sz=size[edge[i].to]; root=0; 
                DFSRoot(edge[i].to,x); 
                Divide(root); 
            } 
} 
int log2[MAXM],dp[20][MAXM]; 
int Max(int x,int y) {return D[x]>D[y]? x:y;} 
void ST() 
{ 
    log2[0]=-1; 
    for (int i=1; i<=N; i++) 
        if (i&(i-1)) log2[i]=log2[i-1]; else log2[i]=log2[i-1]+1; 
    for (int i=1; i<=dfn; i++) dp[0][i]=i; 
    for (int j=1; (1<<j)<=dfn; j++) 
        for (int i=1; i+(1<<j)-1<=dfn; i++) 
            dp[j][i]=Max(dp[j-1][i],dp[j-1][i+(1<<j-1)]); 
} 
inline int RMQ(int l,int r) 
{ 
    int tmp=log2[r-l+1]; 
    return Max(dp[tmp][l],dp[tmp][r-(1<<tmp)+1]);  
} 
struct HeapNode 
{ 
    int ip,L,R,pos; 
    HeapNode (int ip=0,int L=0,int R=0,int pos=0) 
        : ip(ip),L(L),R(R),pos(pos) {} 
    bool operator < (const HeapNode & A) const
        {return D[ip]+D[pos]<D[A.ip]+D[A.pos];} 
}; 
priority_queue<HeapNode>heap; 
int main() 
{ 
    N=read(),M=read(); 
    for (int x,y,z,i=1; i<=N-1; i++) x=read(),y=read(),z=read(),InsertEdge(x,y,z); 
    maxx[root=0]=Sz=N; 
    DFSRoot(1,0); 
    Divide(root); 
    ST(); 
    for (int i=1; i<=dfn; i++) 
        heap.push( HeapNode(i,L[i],R[i],RMQ(L[i],R[i])) ); 
    while (M--) 
        { 
            HeapNode now=heap.top(); heap.pop(); 
            printf("%d\n",D[now.ip]+D[now.pos]); 
            HeapNode ls=now; ls.R=now.pos-1; 
            if (ls.R>=ls.L) ls.pos=RMQ(ls.L,ls.R),heap.push(ls); 
            HeapNode rs=now; rs.L=now.pos+1; 
            if (rs.R>=rs.L) rs.pos=RMQ(rs.L,rs.R),heap.push(rs);      
        } 
    return 0; 
}

一天前刚写超级钢琴,写起来就非常顺畅了

posted @ 2016-08-20 09:04  DaD3zZ  阅读(95)  评论(0编辑  收藏