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;

}


 

 

你可能感兴趣的:(tree)