[WC2010]重建计划 长链剖分 + 线段树

二分以后长链剖分 + 线段树, 扣了半天常数。

好像还用啥nb迭代优化一下二分。

#include

using namespace std;

const int N = (int)1e5 + 7;
const double eps = 1e-9;

int n, L, U;
int head[N], edge_tot;
int len[N], son[N], w[N];
int in[N], dfs_clock;
double tmp[N], add[N];
double cost;
bool ok;

struct Edge {
    int to, w, nex;
} e[N << 1];

inline void addEdge(int u, int v, int w) {
    e[edge_tot].to = v;
    e[edge_tot].w = w;
    e[edge_tot].nex = head[u];
    head[u] = edge_tot++;
}

#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1

struct Tree {
    double mx[N << 2], lazy[N << 2];
    void build(int l, int r, int rt) {
        lazy[rt] = 0;
        mx[rt] = -1e18;
        if(l == r) return;
        int mid = l + r >> 1;
        build(lson); build(rson);
    }
    void update2(int p, double val, int l, int r, int rt) {
        mx[rt] = max(mx[rt], val);
        if(l == r) return;
        int mid = l + r >> 1;
        if(p <= mid) update2(p, val, lson);
        else update2(p, val, rson);
    }
    double query(int L, int R, int l, int r, int rt) {
        if(R < l || r < L || R < L) return -1e18;
        if(L <= l && r <= R) return mx[rt];
        int mid = l + r >> 1;
        return max(query(L, R, lson), query(L, R, rson));
    }
} Tree;

void gao(int u, int fa) {
    son[u] = len[u] = 0;
    for(int o = head[u]; ~o; o = e[o].nex) {
        int v = e[o].to;
        if(v == fa) continue;
        w[v] = e[o].w;
        gao(v, u);
        if(len[v] > len[u]) {
            len[u] = len[v];
            son[u] = v;
        }
    }
    len[u]++;
}

void dfs1(int u, int fa) {
    in[u] = ++dfs_clock;
    if(son[u]) dfs1(son[u], u);
    for(int o = head[u]; ~o; o = e[o].nex) {
        int v = e[o].to;
        if(v == fa || v == son[u]) continue;
        dfs1(v, u);
    }
}

void dfs2(int u, int fa) {
    if(ok) return;
    add[u] = 0;
    if(son[u]) {
        dfs2(son[u], u);
        Tree.update2(in[u], -add[son[u]] - w[son[u]] + cost, 1, n, 1);
        add[u] = add[son[u]] + w[son[u]] - cost;
    }
    else {
        Tree.update2(in[u], 0, 1, n, 1);
    }
    int l = in[u] + L;
    int r = min(in[u] + len[u] - 1, in[u] + U);
    if(Tree.query(l, r, 1, n, 1) + add[u] >= 0.0) ok = true;
    for(int o = head[u], v, w; ~o && !ok; o = e[o].nex) {
        v = e[o].to, w = e[o].w;
        if(v == fa || v == son[u]) continue;
        dfs2(v, u);
        for(int i = 1; i <= len[v] && i <= U && !ok; i++) {
            int l = max(0, L - i), r = min(len[u] - 1, U - i);
            tmp[in[v] + i - 1] = Tree.query(in[v] + i - 1, in[v] + i - 1, 1, n, 1) + add[v] + w - cost;
            if(tmp[in[v] + i - 1] + Tree.query(in[u] + l, in[u] + r, 1, n, 1) + add[u] >= 0.0) {
                ok = true;
            }
        }
        for(int i = 1; i <= len[v] && i <= U && !ok; i++) {
            Tree.update2(in[u] + i, tmp[in[v] + i - 1]- add[u], 1, n, 1);
        }
    }
}

int main() {
    scanf("%d%d%d", &n, &L, &U);
    for(int i = 1; i <= n; i++) head[i] = -1;
    for(int i = 1; i < n; i++) {
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        addEdge(u, v, w);
        addEdge(v, u, w);
    }
    gao(1, 0);
    dfs1(1, 0);
    double low = 0, high = 1000001, mid;
    for(int o = 0; o <= 30; o++) {
        mid = (low + high) / 2;
        cost = mid; ok = false;
        Tree.build(1, n, 1);
        dfs2(1, 0);
        if(ok) low = mid;
        else high = mid;
    }
    printf("%.3f\n", (low + high) / 2);
    return 0;
}

 

你可能感兴趣的:([WC2010]重建计划 长链剖分 + 线段树)