[BZOJ 3784][树上的路径][点分治+堆]
题目大意:
给定一个
N
个结点的树,结点用正整数
思路:
这道题和[BZOJ2006 超级钢琴]类似,没有过的同学可以先切那道题。
http://blog.youkuaiyun.com/g1n0st/article/details/56669953
考虑树是一条序列的情况,将
考虑所有情况,我们先对树进行点分治,对于树的每一个重心,计算这个重心到其所管辖的所有节点的距离,按长度降序排序后分别编号为 1…n , d[i] 表示 dist(i,g) 的长度( g 为当前重心),那么按照一条序列的情况:
对于所有的重心
时,我们将 d[i]+d[j] 输出。而当 i+1 仍被重心 g 管辖时,我们在堆中加入
输出 M 个数后算法结束。
//但是貌似每个重心都要加入
我们将重心分层考虑,总共有
log(n)
层重心,每层有
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.youkuaiyun.com/u014609452/article/details/56669183
完。
By g1n0st