BZOJ 2599 [IOI2011]Race【Tree,点分治】

给出N(1 <= N <= 200000)个结点的树,求长度等于K(1 <= K <= 1000000)的路径的最小边数。

点分治,这道题目和POJ 2114很接近,2114是求是否存在长度为K的边,但是那个K比较大。但是这道题目的K比之小了10倍。

1. 用V[i]表示到当前树根root的路径长度为i 时的点(赋值为root结点即可),这样就可以用来判断两条到根的路径长度之和是否等于K:

    结点a的root的距离为i,结点b到root的距离为j,处理完a之后会得到V[i] = root,那么在处理结点b的时候,如果V[K-j] = root,就说明某一个a和b的路径长度为K,此时,就可以更新最小边数了。

2. e[i]表示到当前树根root的路径长度为i 时的边的最小条数。


 

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <iostream>
using namespace std;
#define N 200010
#define inf 0x3f3f3f3f
struct node {
    int v, l;
    node() {}
    node(int _v, int _l): v(_v), l(_l) {};
};
vector<node> g[N];
int n, K, cur, root, size, ans;
int s[N], f[N], d[N], e[N];  //s子树的结点数,f求重心,d子结点到根的距离,e子结点到根的边数
int v[N*10], c[N*10];
bool done[N];

void getroot(int now, int fa) {
    int u;
    s[now] = 1, f[now] = 0;
    for (int i=0; i<g[now].size(); i++)
        if ((u = g[now][i].v) != fa && !done[u]) {
            getroot(u, now);
            s[now] += s[u];
            f[now] = max(f[now], s[u]);
        }
    f[now] = max(f[now], size-s[now]);
    if (f[now] < f[root]) root = now;
}
void dfs1(int now, int fa) {
    if (d[now] > K) return ;
    if (v[K-d[now]] == cur) ans = min(ans, c[K-d[now]]+e[now]);
    int u;
    for (int i=0; i<g[now].size(); i++)
        if ((u = g[now][i].v) != fa && !done[u]) {
            d[u] = d[now] + g[now][i].l;
            e[u] = e[now] + 1;
            dfs1(u, now);
        }
}
void dfs2(int now, int fa) {
    if (d[now] > K) return ;
    if (v[d[now]] != cur) {
        c[d[now]] = e[now];
        v[d[now]] = cur;
    } else c[d[now]] = min(c[d[now]], e[now]);
    int u;
    for (int i=0; i<g[now].size(); i++)
        if ((u = g[now][i].v) != fa && !done[u])
            dfs2(u, now);
}
void work(int now) {
    v[0] = cur = now + 1;
    int u;
    for (int i=0; i<g[now].size(); i++)
        if (!done[u = g[now][i].v]) {
            d[u] = g[now][i].l;
            e[u] = 1;
            dfs1(u, now);
            dfs2(u, now);
        }
    getroot(now, n); //更新s数组
    done[now] = true;
    for (int i=0; i<g[now].size(); i++)
        if (!done[u = g[now][i].v]) {
            f[n] = size = s[u];
            getroot(u, root=n);
            work(root);
        }
}
int main() {
    scanf("%d%d", &n, &K);
    for (int i=0; i<=n; i++) g[i].clear();

    for (int i=1, a, b, c; i<n; i++) {
        scanf("%d%d%d", &a, &b, &c);
        g[a].push_back(node(b, c));
        g[b].push_back(node(a, c));
    }
    memset(done, false, sizeof(done));

    ans = f[n] = size = n;
    getroot(0, root=n);
    work(root);

    printf("%d\n", ans < n ? ans : -1);

    return 0;
}


 

 

posted on 2013-08-16 18:47  you Richer  阅读(298)  评论(0编辑  收藏  举报