[BZOJ 3784][树上的路径][点分治+堆]
2017-02-23 14:02
357 查看
[BZOJ 3784][树上的路径][点分治+堆]
题目大意:
给定一个N个结点的树,结点用正整数1…N编号。每条边有一个正整数权值。用dist(a,b)表示从结点a到结点b路边上经过边的权值。其中要求b>a。将这n(n+1)2个距离从大到小排序,输出前M个距离值。思路:
这道题和[BZOJ2006 超级钢琴]类似,没有过的同学可以先切那道题。http://blog.csdn.net/g1n0st/article/details/56669953
考虑树是一条序列的情况,将dist(1…n,n)加入堆,每次取出dist(i,j)计入答案后将dist(i+1,j)继续加入。
考虑所有情况,我们先对树进行点分治,对于树的每一个重心,计算这个重心到其所管辖的所有节点的距离,按长度降序排序后分别编号为1…n,d[i]表示dist(i,g)的长度(g为当前重心),那么按照一条序列的情况:
对于所有的重心g,我们将(g,1,1),(g,1,2),…,(g,1,n−1),(g,1,n)入堆,每次取出(g,i,j),当:
i<j
i和j被同一个重心g管辖
i和j属于同一个重心的不同子树
时,我们将d[i]+d[j]输出。而当i+1仍被重心g管辖时,我们在堆中加入(g,i+1,j)。
输出M个数后算法结束。
//但是貌似每个重心都要加入n个数,总共不会有N2个点对入堆吗?
我们将重心分层考虑,总共有log(n)层重心,每层有n个点(一个点不会在同一层内被两个重心所管辖),所以总共只会有nlog(n)个点对在堆中。
点分复杂度O(nlogn),枚举复杂度O(klogn),总复杂度O(n+klogn)。
代码:
#include <bits/stdc++.h> const int Maxn = 100010; using namespace std; inline char get(void) { static char buf[1000000], *p1 = buf, *p2 = buf; if (p1 == p2) { p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin); if (p1 == p2) return EOF; } return *p1++; } inline void read(int &x) { x = 0; static char c; for (; !(c >= '0' && c <= '9'); c = get()); for (; c >= '0' && c <= '9'; x = x * 10 + c - '0', c = get()); } struct Abcd { int x, y, type, data; friend bool operator < (const Abcd &a, const Abcd &b) { return a.data > b.data; } } tmp; multiset<Abcd> s; int head[Maxn], sub; int stk[Maxn], top, dist[Maxn], siz[Maxn], km, num[20]; int d[Maxn][20], belong[Maxn][20], g[Maxn][20], b[Maxn][20]; bool vis[Maxn]; struct Edge { int to, nxt, v; Edge(void) {} Edge(const int &to, const int &nxt, const int &v) : to(to), nxt(nxt), v(v) {} } edge[Maxn << 1]; inline void add(int a, int b, int v) { edge[++sub] = Edge(b, head[a], v), head[a] = sub; } inline void dfs(int u, int fa) { siz[u] = 1; stk[++top] = u; for (int i = head[u], v; i; i = edge[i].nxt) { v = edge[i].to; if (v == fa || vis[v]) continue; dfs(v, u); siz[u] += siz[v]; } } inline void dg(int u, int fa, int type) { for (int i = head[u], v; i; i = edge[i].nxt) { v = edge[i].to; if (v == fa || vis[v]) continue; d[v][type] = d[u][type] + edge[i].v; belong[v][type] = belong[u][type]; dg(v, u, type); } } inline bool cmp(int x, int y) { return d[x][km] > d[y][km]; } void solve(int x, int type) { top = 0; dfs(x, 0); int i, j = x, k = 0, t, v; while (1) { for (i = head[j]; i; i = edge[i].nxt) { v = edge[i].to; if (!vis[v] && v != k && siz[v] > top >> 1) { k = j; j = v; break; } } if (!i) break; } for (int i = head[j], v; i; i = edge[i].nxt) { v = edge[i].to; if (!vis[v]) { belong[v][type] = v; d[v][type] = edge[i].v; dg(v, j, type); } } for (int i = 1; i <= top; i++) g[stk[i]][type] = j; km = type; sort(stk + 1, stk + top + 1, cmp); for (int i = 1; i <= top; i++) { tmp.x = i + num[type]; tmp.y = 1 + num[type]; tmp.type = type; tmp.data = d[stk[i]][type] + d[stk[1]][type]; s.insert(tmp); } for (int i = num[type] + 1; i <= num[type] + top; i++) b[i][type] = stk[i - num[type]]; num[type] += top; vis[j] = 1; for (int i = head[j], v; i; i = edge[i].nxt) { v = edge[i].to; if (!vis[v]) solve(v, type + 1); } } int n, m; int main(void) { //freopen("b.in", "r", stdin); //freopen("b.out", "w", stdout); read(n), read(m); for (int i = 1, a, b, v; i < n; i++) { read(a), read(b), read(v); add(a, b, v); add(b, a, v); } solve(1, 0); int ans = 0; while (m--) { while (1) { tmp = *s.begin(); s.erase(s.begin()); int j = tmp.x, k = tmp.y, l = tmp.type, t = tmp.data; if (k + 1 <= num[l] && g[b[j][l]][l] == g[b[k + 1][l]][l]) { tmp.data = d[b[j][l]][l] + d[b[k + 1][l]][l]; tmp.y = k + 1; s.insert(tmp); } if (belong[b[j][l]][l] != belong[b[k][l]][l] && g[b[j][l]][l] == g[b[k][l]][l] && b[j][l] < b[k][l]) { ans = t; break; } } printf("%d\n", ans); } //fclose(stdin), fclose(stdout); return 0; }
附Manchery学长的可持久化可并堆(%%%BZOJ rank1)做法:
http://blog.csdn.net/u014609452/article/details/56669183
完。
By g1n0st
相关文章推荐
- [BZOJ3784]树上的路径 点分治序+ST表
- 【BZOJ-3784】树上的路径 点分治 + ST + 堆
- [BZOJ3784]树上的路径(点分治+dfs序+st表+堆)
- 【BZOJ3784】树上的路径 点分治序+ST表
- bzoj 3784: 树上的路径【点分治+st表+堆】
- 【bzoj3784】【树上的路径】【点分治+堆+st表】
- [bzoj3784][点分治]树上的路径
- bzoj 3784: 树上的路径 点分治+RMQ+堆
- [可持久化可并堆 || ST表 点分治] BZOJ 3784 树上的路径
- BZOJ 3784|树上的路径|点分治|堆|RMQ
- bzoj 3784: 树上的路径 堆维护第k大
- 树上的路径 BZOJ 3784
- bzoj 3784: 树上的路径 (ST表+优先队列+点分治)
- 【bzoj3784】树上的路径
- [BZOJ3784]树上的路径(点分治+STL)
- bzoj3784 树上的路径
- bzoj 3784: 树上的路径
- 【BZOJ 3697】采药人的路径 树上点分治
- [bzoj3784]树上的路径
- BZOJ3784 : 树上的路径