[BZOJ 3784] 树上的路径

[题目链接]

         https://www.lydsy.com/JudgeOnline/problem.php?id=3784

[算法]

       首先简单介绍一下点分治序列 :

       点分治序列和DFS序列,BFS序列等类似,是将点分治过程中每次的分治中心连在一起形成的序列,显然,点分治序列的长度是(N log N)的,那么,这个序列有什么用处呢? 当我们确定了一个分治中心后,与这个分治中心能形成一段路径的,必然是分治中心或是分治中心的一条链,是点分治序列

        中连续的一段

        在这题中,我们先求出点分治序列和每个节点与分治中心能形成路径的区间,再求出每个点到分治中心的距离,然后问题就由树上转化为了序列上 :

        在若干个数中,每个数都可以与它“控制”的一段区间的某个数相加,求前m个这样的最大的值

        这个问题可以用Sprase Table配合堆来解决

        时间复杂度 : O((N + M) log N)
[代码]

       

#include<bits/stdc++.h>
using namespace std;
#define MAXN 50010
#define MAXS 800010
#define MAXLOG 20

struct info
{
    int val,pos,l,r;
    friend bool operator < (info a,info b)
    {
        return a.val < b.val;
    }
} ;

struct Edge
{
    int to,w,nxt;
} e[MAXN<<1];

int i,j,n,m,pos,m1,m2,u,v,w,cnt,root,tot;
int size[MAXN],weight[MAXN],head[MAXN],val[MAXS],l[MAXS],r[MAXS],sum[MAXS];
int mx[MAXS][MAXLOG];
bool visited[MAXN];
priority_queue< info > q;
info tmp;

inline void addedge(int u,int v,int w)
{
    cnt++;
    e[cnt] = (Edge){v,w,head[u]};
    head[u] = cnt;
}
inline void getroot(int u,int fa,int total)
{
    int i,v;
    size[u] = 1;
    weight[u] = 0;
    for (i = head[u]; i; i = e[i].nxt)
    {
        v = e[i].to;
        if (fa != v && !visited[v])
        {
            getroot(v,u,total);
            size[u] += size[v];
            weight[u] = max(weight[u],size[v]);
        }
    }
    weight[u] = max(weight[u],total - size[u]);
    if (weight[u] < weight[root]) root = u;
}
inline void dfs(int u,int fa)
{
    int i,v,w;
    tot++;
    val[tot] = sum[u];
    l[tot] = l[tot-1]; 
    if (!r[tot]) r[tot] = r[tot-1]; 
    for (i = head[u]; i; i = e[i].nxt)
    {
        v = e[i].to;
        w = e[i].w;
        if (v != fa && !visited[v])
        {
            sum[v] = sum[u] + w;
            dfs(v,u);
        }
    }
}
inline void work(int u)
{
    int i,v,w;
    visited[u] = true;
    tot++;
    val[tot] = 0;
    l[tot] = tot; r[tot] = tot - 1;
    for (i = head[u]; i; i = e[i].nxt)
    {
        v = e[i].to;
        w = e[i].w;
        if (!visited[v]) 
        {
            sum[v] = w; 
            r[tot+1] = tot;
            dfs(v,u);
        }
    }    
    for (i = head[u]; i; i = e[i].nxt)
    {
        v = e[i].to;
        if (!visited[v])
        {
            root = 0;
            getroot(v,u,size[v]);
            work(root);
        }
    }
}
inline int my_max(int x,int y)
{
    return (val[x] > val[y]) ? x : y;
}
inline int query(int l,int r)
{
    if (l > r) return -1;
    int k = log(r - l + 1) / log(2.0);    
    return my_max(mx[l][k],mx[r-(1<<k)+1][k]);
}

int main()
{
    
    scanf("%d%d",&n,&m);
    for (i = 1; i < n; i++)
    {
        scanf("%d%d%d",&u,&v,&w);
        addedge(u,v,w);
        addedge(v,u,w);
    }
    root = 0;
    size[0] = weight[0] = n;
    getroot(1,0,n);
    work(root);
    for (i = 1; i <= tot; i++) mx[i][0] = i;
    for (j = 1; (1 << j) <= tot; j++)
    {
        for (i = 1; i + (1 << j) - 1 <= tot; i++)    
        {
            mx[i][j] = my_max(mx[i][j-1],mx[i+(1<<(j-1))][j-1]);
        }
    }
    for (i = 1; i <= tot; i++)
    {
        if (l[i] <= r[i])
            q.push((info){val[query(l[i],r[i])] + val[i],i,l[i],r[i]}); 
    }
    for (i = 1; i <= m; i++)
    {
        tmp = q.top();
        q.pop();
        printf("%d\n",tmp.val);
        pos = query(tmp.l,tmp.r);
        m1 = query(tmp.l,pos-1);
        m2 = query(pos+1,tmp.r);
        if (m1 != -1) q.push((info){val[m1] + val[tmp.pos],tmp.pos,tmp.l,pos-1});
        if (m2 != -1) q.push((info){val[m2] + val[tmp.pos],tmp.pos,pos+1,tmp.r});
    }
    
    return 0;
}

 

         

       

posted @ 2018-07-18 15:09  evenbao  阅读(237)  评论(0编辑  收藏  举报