P2491 [SDOI2011]消防 [树的直径][two-pointers]

P2491 [SDOI2011]消防

这道题有点东西

首先,这道题如果有了这么个结论就没二分答案什么事情了:

这条长度不超过\(s\)的路径一定在直径上

所以就是和“树网的核”一个思路:在直径上面跑two-pointers,因为长度越长显然答案更优。

我们先求个直径,用两次dfs来求。

之后在直径上尺取的时候,当得到一段区间的时候,把直径两个端点到当前区间两个端点的距离计入答案。这里考虑直径内部对答案的贡献。

接下来考虑直径之外对答案的贡献。我们直接从直径上所有的点上面dfs,从所有不在直径上的点到直径的距离计入答案。

然后就完事了。我不会二分答案怎么做

代码:

/*************************************************************************
 @Author: Garen
 @Created Time : Tue 19 Feb 2019 08:05:33 AM CST
 @File Name: P2491.cpp
 @Description:
 ************************************************************************/
#include<bits/stdc++.h>
#define ll long long
const int maxn = 300005;
std::vector<std::pair<int,int> > G[maxn];
int fa[maxn], dep[maxn], weight[maxn];
int a[maxn], b[maxn], tot;
bool vis[maxn];
int n, s;

void bfs(int start, int &maxdep, int &idx) {
    memset(dep, -1, sizeof dep);
    memset(fa, 0, sizeof fa);
    std::queue<int> q;
    dep[start] = 0; q.push(start);
    while(!q.empty()) {
        int u = q.front(); q.pop();
        if(dep[u] > maxdep) {
            maxdep = dep[u]; idx = u;
        }
        for(auto it: G[u]) {
            int v = it.first, w = it.second;
            if(dep[v] == -1) {
                dep[v] = dep[u] + w; fa[v] = u;
                weight[v] = w;
                q.push(v);
            }
        }
    }
}
void get_diameter() {
    int maxdep = -1, idx = -1;
    bfs(1, maxdep, idx);
    int temp = idx;
    maxdep = -1, idx = -1;
    bfs(temp, maxdep, idx);
    while(idx != temp) {
        a[++tot] = idx;
        b[tot] = weight[idx];
        idx = fa[idx];
    }
    a[++tot] = idx;
}
void dfs(int u, int f) {
    fa[u] = f;
    for(auto it: G[u]) {
        int v = it.first, w = it.second;
        if(vis[v] || v == f) continue;
        dep[v] = dep[u] + w;
        dfs(v, u);
    }
}
int main() {
    scanf("%d %d", &n, &s);
    for(int i = 1; i < n; i++) {
        int u, v, w; scanf("%d %d %d", &u, &v, &w);
        G[u].push_back(std::make_pair(v, w));
        G[v].push_back(std::make_pair(u, w));
    }
    get_diameter();
    //for(int i = 1; i <= tot; i++) printf("%d ", a[i]);
    int ans = 0x3f3f3f3f;
    for(int l = 1, r = 0, sum = 0; l <= tot; l++) {
        vis[a[l]] = true;
        while(sum + b[r] <= s && r + 1 <= tot) {
            sum += b[r]; r++;
        }
        //printf("%d %d\n", l, r);
        ans = std::min(ans, std::max(dep[a[r]], dep[a[1]] - dep[a[l]]));
        sum -= b[l];
    }
    for(int i = 1; i <= tot; i++) {
        dep[a[i]] = 0; dfs(a[i], 0);
    }
    for(int i = 1; i <= n; i++) if(!vis[i]) ans = std::max(ans, dep[i]);
    printf("%d\n", ans);
    return 0;
}
posted @ 2019-02-19 21:27  Garen-Wang  阅读(113)  评论(0编辑  收藏  举报