Race[IOI2011]

【题目描述】
给一棵树,每条边有权.求一条简单路径,权值和等于\(K\),且边的数量最小。

【输入格式】
第一行 两个整数 \(n, k\)
\(2\dots n\)行 每行三个整数 表示一条无向边的两端和权值 (注意点的编号从\(0\)开始)

【输出格式】
一个整数 表示最小边数量 如果不存在这样的路径 输出\(-1\)

题解

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#pragma GCC optimize("Ofast") 
#define N 200005
using namespace std;
typedef long long ll;

inline int read() {
    int x = 0, f = 1; char ch = getchar();
    for (; ch > '9' || ch < '0'; ch = getchar()) if (ch == '-') f = -1;
    for (; ch <= '9' && ch >= '0'; ch = getchar()) x = (x << 3) + (x << 1) + (ch ^ '0');
    return x * f;
}

const int inf = 0x3f3f3f3f;
int n, k, rt, nowsz, mx;
int head[N], pre[N<<1], to[N<<1], val[N<<1], sz; 
int tot[N], dis[N], dis2[N];
int ans;
bool vis[N];
int p[N], q[N], buc[1000005], top, lst;

inline void init() {
    buc[0] = 0;
    for (int i = 1; i <= k; i++) buc[i] = inf;
    ans = inf;
}

inline void addedge(int u, int v, int w) {
    pre[++sz] = head[u]; head[u] = sz; to[sz] = v; val[sz] = w; 
    pre[++sz] = head[v]; head[v] = sz; to[sz] = u; val[sz] = w; 
}

void getrt(int x, int fa) {
    tot[x] = 1;
    int nowmx = -inf;
    for (int i = head[x]; i; i = pre[i]) {
        int y = to[i];
        if (y == fa || vis[y]) continue;
        getrt(y, x);
        tot[x] += tot[y];
        nowmx = max(nowmx, tot[y]);
    }
    nowmx = max(nowmx, nowsz - tot[x]);
    if (nowmx < mx) {
        mx = nowmx, rt = x;
    }
}

void getdis(int x, int fa) {
    if (dis[x] > k) return;
    p[++top] = dis[x]; q[top] = dis2[x];
    for (int i = head[x]; i; i = pre[i]) {
        int y = to[i];
        if (y == fa || vis[y]) continue;
        dis[y] = dis[x] + val[i];
        dis2[y] = dis2[x] + 1;
        getdis(y, x);
    }
}

void calc(int x) {
    dis[x] = dis2[x] = 0;
    top = 0; buc[0] = 0;
    for (int i = head[x]; i; i = pre[i]) {
        int y = to[i];
        if (vis[y]) continue;
        lst = top + 1;
        dis[y] = dis[x] + val[i]; 
        dis2[y] = dis2[x] + 1;
        getdis(y, x); 
        for (int j = lst; j <= top; j++) {
            ans = min(ans, q[j] + buc[k - p[j]]);
        }
        for (int j = lst; j <= top; j++) {
            buc[p[j]] = min(buc[p[j]], q[j]);
        }
    }
    for (int i = 1; i <= top; i++) {
        buc[p[i]] = inf;
    }
}

void divide(int x) {
    calc(x);
    vis[x] = 1;
    for (int i = head[x]; i; i = pre[i]) {
        int y = to[i];
        if (vis[y]) continue;
        mx = inf;
        nowsz = tot[y];
        getrt(y, 0);
        divide(rt); 
    } 
}

int main() {
    n = read(); k = read();
    init();
    for (int i = 1; i < n; i++) {
        int u = read() + 1, v = read() + 1, w = read();
        addedge(u, v, w);
    }
    mx = inf;
    nowsz = n;
    getrt(1, 0);
    divide(rt);
    printf("%d\n", ans == inf ? -1 : ans);
    return 0;
} 

猜你喜欢

转载自www.cnblogs.com/ak-dream/p/AK_DREAM50.html
今日推荐