「Luogu P2664」树上游戏

给一棵树,每条边有权。求一条简单路径,权值和等于 K ,且边的数量最小。

Luogu

分析

显然这题是点分治。

我们设 f[i] 表示当前的子树中,距离 rt 为 i 的最短距离,每次在求出 rt 到它子树内的所有 dis 并同时将子树中的节点加入栈 s 中,然后对 ans 取 min ,设 dep[i] 表示 i 在 rt 的子树中的深度,我们便令 ans = min(ans, f[k-dis[s[i]]] + dep[s[i]]) ,然后再更新 f[i] , f[dis[s[i]]] = min(f[dis[s[i]]], dep[s[i]]) 。

代码

//=========================
//  author:hliwen
//  date:2020.1.19
//=========================
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 200005
#define M 1000003
#define INF 0x3f3f3f3f
#define DEBUG puts("ok")
#define tie0 cin.tie(0),cout.tie(0)
#define fastio ios::sync_with_stdio(false)
#define File(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout)
using namespace std;
typedef long long ll;

template <typename T> inline void read(T &x) {
    T f = 1; x = 0; char c;
    for (c = getchar(); !isdigit(c); c = getchar()) if (c == '-') f = -1;
    for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
    x *= f;
}

int n, k, size, rt, ans, tot, num;
int to[N<<1], nxt[N<<1], val[N<<1], head[N<<1], cnt;
int son[N], sz[N], dis[N], f[M], st[N], s[N], dep[N];
bool vis[N];

void insert(int u, int v, int w) {
    to[++cnt] = v, val[cnt] = w, nxt[cnt] = head[u], head[u] = cnt;
}

void dfs_root(int u, int fa) {
    sz[u] = 1, son[u] = 0;
    for (int i = head[u]; i; i = nxt[i]) {
        int v = to[i];
        if (v == fa || vis[v]) continue;
        dfs_root(v, u);
        sz[u] += sz[v];
        son[u] = max(son[u], sz[v]);
    }
    son[u] = max(son[u], size - sz[u]);
    if (son[rt] > son[u]) rt = u;
}

void dfs_dis(int u, int fa, int len) {
    dis[u] = len, dep[u] = dep[fa] + 1, s[++tot] = u;
    for (int i = head[u]; i; i = nxt[i]) {
        int v = to[i], w = val[i];
        if (vis[v] || v == fa) continue;
        dfs_dis(v, u, len + w);
    }
}

void solve(int u) {
    num = f[0] = dep[u] = 0;
    for (int i = head[u]; i; i = nxt[i]) {
        int v = to[i], w = val[i];
        if (vis[v]) continue;
        tot = 0;
        dfs_dis(v, u, w);
        for (int j = 1; j <= tot; ++j)
            if (dis[s[j]] <= k)
                ans = min(ans, f[k-dis[s[j]]] + dep[s[j]]);
        for (int j = 1; j <= tot; ++j)
            if (dis[s[j]] <= k) {
                f[dis[s[j]]] = min(f[dis[s[j]]], dep[s[j]]);
                st[++num] = dis[s[j]];
            }
    }
    for (int i = 1; i <= num; ++i) f[st[i]] = INF;
}

void divide(int u) {
    vis[u] = 1;
    solve(u);
    for (int i = head[u]; i; i = nxt[i]) {
        int v = to[i];
        if (vis[v]) continue;
        size = sz[v], rt = 0;
        dfs_root(v, u);
        divide(rt);
    }
}

int main() {
    int u, v, w;
    memset(f, INF, sizeof f);
    read(n), read(k);
    for (int i = 1; i < n; ++i) {
        read(u), read(v), read(w);
        insert(u + 1, v + 1, w), insert(v + 1, u + 1, w);
    }
    rt = 0, son[0] = size = n, ans = n + 1;
    dfs_root(1, 0);
    divide(rt);
    if (ans >= n + 1) puts("-1");
    else printf("%d", ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/hlw1/p/12214734.html