[BZOJ2117][[2010国家集训队]Crash的旅游计划][点分治+二分答案]

50 篇文章 0 订阅
6 篇文章 0 订阅

[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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值