[IOI2011]Race ---- 点分治

传送门:洛谷P4149


题目描述

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


分析

  模板基本一致,主要讲讲calc:
  calc有两种模式,这里用的是子树间的逐个处理(保证都进过根节点).
  由于要求边数量最小,在加上k值的范围不算太大,因此我们可以直接用数组tot[x]记录距根节点距离为x的最短边数.
  PS:总共要访问子树三遍:
    第一次利用当前子树节点与前面子树节点更新ans
    第二次是将当前子树变为前子树(更新记录){一二交替进行}
    第三次是在前面两次结束后,将整棵子树标记还原
    (处理tot[0] = 0 外,其余值均为inf)


代码

#include <cstdio>
#include <cstdlib>
#include <cstring>

using namespace std;

IL int read()
{
    char c = getchar();
    int sum = 0 ,k = 1;
    for(;'0' > c || c > '9'; c = getchar())
        if(c == '-') k = -1;
    for(;'0' <= c && c <= '9'; c = getchar()) sum = sum * 10 + c - '0';
    return sum * k;
}

struct Edge
{
    int to, nxt, w;
    IL Edge(int to_ = 0, int nxt_ = 0, int w_ = 0)
    {
        to = to_; nxt = nxt_; w = w_;
    }
}e[400005];
int cnt;
int last[200005];
IL void add(int u, int v, int w)
{
    e[++cnt] = Edge(v, last[u], w); last[u] = cnt;
    e[++cnt] = Edge(u, last[v], w); last[v] = cnt; 
}

int n, m;
int inf;
int ans;
int tot[1000005];
int size[200005];
bool use[200005];

IL int min_(int x, int y) { return x < y ? x : y; }
IL int max_(int x, int y) { return x > y ? x : y; }

IL void get_root(int u, int pre, int &root, int &k, int sz)
{
    size[u] = 1;
    int tmp = 0;
    for(int i = last[u], v; i; i = e[i].nxt)
    if(e[i].to != pre && !use[e[i].to])
    {
        v = e[i].to;
        get_root(v, u, root, k, sz);
        tmp = max_(tmp, size[v]);
        size[u] += size[v];
    }
    tmp = max_(tmp, sz - size[u]);
    if(tmp < k) { k = tmp; root = u; }
}

IL void get_dis(int u, int pre, int dis, int dep, int k)
{
    if(k == 1) ans = min_(ans, tot[m - dis] + dep); else
    if(k == 2) tot[dis] = min_(tot[dis], dep); else
    tot[dis] = inf;
    for(int i = last[u]; i; i = e[i].nxt)
    if(e[i].to != pre && !use[e[i].to] && dis + e[i].w <= m)
        get_dis(e[i].to, u, dis + e[i].w, dep + 1, k);
}

IL void calc(int u)
{
    for(int i = last[u]; i; i = e[i].nxt)
    if(!use[e[i].to] && e[i].w <= m)
    {
        get_dis(e[i].to, u, e[i].w, 1, 1);
        get_dis(e[i].to, u, e[i].w, 1, 2);
    }
    for(int i = last[u]; i; i = e[i].nxt)
    if(!use[e[i].to] && e[i].w <= m)
    {
        get_dis(e[i].to, u, e[i].w, 1, 3);
    }
    tot[0] = 0;
}

IL void solve(int p, int sz)
{
    int root = -1, k = sz;
    get_root(p, 0, root, k, sz);
    use[root] = 1;

    calc(root);

    for(int i = last[root]; i; i = e[i].nxt)
    if(!use[e[i].to])
    {
        solve(e[i].to, size[e[i].to]);
    }
}

int main()
{
    n = read(); m = read();
    for(int i = 1, x, y; i < n; ++i)
    {
        x = read(); y = read();
        add(x + 1, y + 1, read());
    }

    memset(tot, 0x3f, sizeof(tot));
    tot[0] = 0;
    ans = inf = tot[1];
    solve(1, n);

    if(ans == inf) ans = -1;
    printf("%d\n", ans);

    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_27121257/article/details/81254877