您的位置:首页 > 其它

[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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Bzoj 点分治 贪心