[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; }