首页 > 代码库 > zoj 3820 Building Fire Stations(二分+bfs)

zoj 3820 Building Fire Stations(二分+bfs)

题目链接:zoj 3820 Building Fire Stations

题目大意:给定一棵树,选取两个建立加油站,问说所有点距离加油站距离的最大值的最小值是多少,并且任意输出一种建立加油站的方式。

解题思路:二分距离判断,判断函数的复杂度是o(n),这样的复杂度应该是o(nlogn),即使常数系数偏大,但是居然跑了4.5s,也是醉了。
判断函数里面做了3次bfs,但是每次bfs节点最多遍历1次,首先任取一点做根建立一个树,找到深度最大的节点,以向上移动L(判断的ans)位置处的节点建立加油站,并以该点为根在建一棵树,这是第2次bfs,然后同样以最深的节点向上移动L次的节点建立加油站,判断这两个加油站使得所有节点距离加油站的值是否均小于等于L。

#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>

using namespace std;

const int maxn = 200005;
const int INF = 0x3f3f3f3f;

int M, first[maxn], jump[maxn * 2], edge[maxn * 2];
int N, s, e, far[maxn], dis[maxn];
queue<int> que;

inline void add_edge(int u, int v) {
    edge[M] = v;
    jump[M] = first[u];
    first[u] = M++;
}

void init () {
    int l, r;
    M = 0;
    scanf("%d", &N);
    memset(first, -1, sizeof(first));
    for (int i = 1; i < N; i++) {
        scanf("%d%d", &l, &r);
        add_edge(l, r);
        add_edge(r, l);
    }
}

int bfs(int s) {
    que.push(s);
    dis[s] = far[s] = 0;

    int u;
    while (!que.empty()) {
        u = que.front();
        que.pop();

        for (int i = first[u]; i + 1; i = jump[i]) {
            int v = edge[i];
            if (dis[v] > dis[u] + 1) {
                far[v] = u;
                dis[v] = dis[u] + 1;
                que.push(v);
            }
        }
    }
    return u;
}

bool judge (int L) {
    memset(dis, INF, sizeof(dis));
    s = bfs(1);
    for (int i = 0; i < L; i++) {
        s = far[s];
        if (far[s] == 0) {
            e = s % N + 1;
            return true;
        }
    }

    memset(dis, INF, sizeof(dis));
    e = bfs(s);
    for (int i = 0; i < L; i++) {
        if (far[e] == 0)
            return true;
        e = far[e];
    }

    if (e == s)
        e = e % N + 1;
    bfs(e);
    for (int i = 1; i <= N; i++)
        if (dis[i] > L)
            return false;
    return true;
}

void solve () {
    if (N == 2) {
        printf("0 1 2\n");
        return ;
    }

    int l = 0, r = N;
    while (l < r) {
        int mid = (l + r) / 2;
        if (judge(mid))
            r = mid;
        else
            l = mid + 1;
    }
    judge(l);
    printf("%d %d %d\n", l, s, e);
}

int main () {
    int cas;
    scanf("%d", &cas);
    while (cas--) {
        init();
        solve();
    }
    return 0;
}

zoj 3820 Building Fire Stations(二分+bfs)