[BZOJ2117][[2010国家集训队]Crash的旅游计划][点分治+二分答案]
题目大意:
求树上每个点第k长的路径。
思路:
这道题和这道题应该是双倍经验:http://blog.csdn.net/g1n0st/article/details/58127733
但是我那道题的代码交到这里来并过不了。
首先还是考虑对树进行分治然后二分答案,但是维护树上路径可以直接用vector而不是线段树,因为并没有对路径的修改,全部维护好后对vector进行sort然后在里面二分就好了。另外也不用另外处理lca会比较慢,考虑到点分树的树高最多只有logn,另外开一个数组记录每个点点分树上的所有祖先就好了。
STL 有 reserve这个函数,可以直接翻转数组
这道题并不用开long long
代码:
#include <bits/stdc++.h>
const int Maxn = 1000010;
using namespace std;
inline int Max(const int &a, const int &b) {
return a > b ? a : b;
}
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; bool f = 0;
for (; !(c >= '0' && c <= '9'); c = get()) if (c == '-') f = 1;
for (; c >= '0' && c <= '9'; x = x * 10 + c - '0', c = get()); if (f) x = -x;
}
inline void write(int x) {
if (!x) return (void)puts("0");
if (x < 0) putchar('-'), x = -x;
static short s[12], t;
while (x) s[++t] = x % 10, x /= 10;
while (t) putchar('0' + s[t--]);
putchar('\n');
}
vector<int> T[Maxn], F[Maxn];
inline int Find(vector<int>&a, int x) {
return lower_bound(a.begin(), a.end(), x) - a.begin();
}
int n, k;
int head[Maxn], sub;
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;
}
int siz[Maxn], son[Maxn], root, S;
bool vis[Maxn];
int fat[Maxn][20], dis[Maxn][20];
inline void getroot(int u, int fa) {
siz[u] = 1; son[u] = 0;
for (int i = head[u], v; i; i = edge[i].nxt) {
v = edge[i].to;
if (v == fa || vis[v]) continue;
getroot(v, u);
siz[u] += siz[v];
son[u] = Max(son[u], siz[v]);
}
son[u] = Max(son[u], S - siz[u]);
if (son[u] < son[root]) root = u;
}
inline void dfs(int cur, int u, int fa, int w) {
fat[u][++*fat[u]] = cur; dis[u][++*dis[u]] = w;
for (int i = head[u], v; i; i = edge[i].nxt) {
v = edge[i].to;
if (vis[v] || v == fa) continue;
dfs(cur, v, u, w + edge[i].v);
}
}
inline void solve(int x) {
vis[x] = 1;
fat[x][++*fat[x]] = x; dis[x][++*dis[x]] = 0;
for (int i = head[x], v; i; i = edge[i].nxt) {
v = edge[i].to;
if (vis[v]) continue;
dfs(x, v, x, edge[i].v);
}
for (int i = head[x], v; i; i = edge[i].nxt) {
v = edge[i].to;
if (vis[v]) continue;
S = siz[v]; root = 0;
getroot(v, 0);
solve(root);
}
}
inline bool check(int x, int mid) {
int ret = 0;
for (int i = 1; i <= *fat[x]; i++) {
ret += Find(T[fat[x][i]], mid - dis[x][i]);
if (i > 1) ret -= Find(F[fat[x][i - 1]], mid - dis[x][i]);
}
return ret < k;
}
int main(void) {
freopen("2117.in", "r", stdin);
freopen("2117.out", "w", stdout);
read(n); read(k); k++;
for (int i = 1, u, v, w; i < n; i++) {
read(u), read(v), read(w);
add(u, v, w), add(v, u, w);
}
son[0] = S = n;
getroot(1, 0);
solve(root);
for (int i = 1; i <= n; i++) {
reverse(fat[i] + 1, fat[i] + *fat[i] + 1), reverse(dis[i] + 1, dis[i] + *dis[i] + 1);
for (int j = 1; j <= *fat[i]; j++) {
T[fat[i][j]].push_back(dis[i][j]);
if (j > 1) F[fat[i][j- 1]].push_back(dis[i][j]);
}
}
for (int i = 1; i <= n; i++) {
sort(F[i].begin(), F[i].end());
sort(T[i].begin(), T[i].end());
}
for (int i = 1; i <= n; i++) {
int L = 0, R = 1 << 30, mid;
while (L < R - 1)
check(i, mid = (L + R) >> 1) ? L = mid : R = mid;
write(R - 1);
}
return 0;
}
完。
By g1n0st