[LUOGU4149][IOI2011]Race

题目描述

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

输入输出格式

输入格式:

第一行:两个整数 n,kn,kn,k 。

第二至 nnn 行:每行三个整数,表示一条无向边的两端和权值 (注意点的编号从 000 开始)。

输出格式:

一个整数,表示最小边数量。

如果不存在这样的路径,输出 −1-11 。

输入输出样例

输入样例#1: 
4 3
0 1 1
1 2 2
1 3 4
输出样例#1: 
2

说明

n≤200000,K≤1000000n\le 200000,K\le 1000000n200000,K1000000 。


 
点分治, 记录路径长度为i的最小经过路径, 然后就是正常的点分治。
之后记得把上面说的数组赋成正无穷, 这个不能暴力清空, 否则复杂度退化。
 

 
 
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
inline int read() {
    int res=0;char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
    return res;
}
#define reg register
#define N 200005
int n, tot, k;
struct edge {
    int nxt, to, val;
}ed[N*2];
int head[N], cnt = 1;
inline void add(int x, int y, int z)
{
    ed[++cnt] = (edge){head[x], y, z};
    head[x] = cnt;
}

bool cut[N*2];
int siz[N], root, mrt = 1e9, maxx[N];
void dfs(int x, int fa)
{
    siz[x] = 1;
    for (reg int i = head[x] ; i ; i = ed[i].nxt)
    {
        int to = ed[i].to;
        if (to == fa or cut[i]) continue;
        dfs(to, x);
        siz[x] += siz[to];
    }    
}

void efs(int x, int fa)
{
    int tmp = tot - siz[x];
    for (reg int i = head[x] ; i ; i = ed[i].nxt)
    {
        int to = ed[i].to;
        if (to == fa or cut[i]) continue;
        efs(to, x);
        tmp = max(tmp, siz[to]);
    }
    if (tmp < mrt) mrt = tmp, root = x;
}

inline int FindRoot(int x)
{
    dfs(x, 0);
    mrt = 1e9;
    tot = siz[x];
    root = n;
    efs(x, 0);
    return root;
}

int ans = 1e9;
int dep[N], len[1000005], dis[N];//深度|长为i的最小深度|长度 

void Work(int x, int fa)
{
    if (dis[x] > k) return ;
    if (len[k - dis[x]] != 0x3f3f3f3f) 
        if (dep[x] + len[k - dis[x]] <= ans) ans = dep[x] + len[k - dis[x]];
    for (reg int i = head[x] ; i ; i = ed[i].nxt)
    {
        int to = ed[i].to;
        if (cut[i] or to == fa) continue;
        dep[to] = dep[x] + 1;
        dis[to] = dis[x] + ed[i].val;
    //    len[dis[to]] = min(len[dis[to]], dep[to]);
        Work(to, x);
    }
}

void Pushup(int x, int fa, bool opt)
{
    if (dis[x] > k) return;
    if (opt) len[dis[x]] = 0x3f3f3f3f; 
    else len[dis[x]] = min(len[dis[x]], dep[x]);
    for (reg int i = head[x] ; i ; i = ed[i].nxt)
    {
        int to = ed[i].to;
        if (to == fa or cut[i]) continue;
        Pushup(to, x, opt);
    }
}

void solve(int rt)
{
    root = FindRoot(rt);
    len[0] = 0;
    //memset(len, 0x3f, sizeof len);
    for (reg int i = head[root] ; i ; i = ed[i].nxt)
    {
        int to = ed[i].to;
        if (cut[i])  continue;
        dep[root] = 0, dep[to] = 1;
        dis[root] = 0, dis[to] = ed[i].val;
        Work(to, root);
        Pushup(to, root, 0);
    }
    for (reg int i = head[root] ; i ; i = ed[i].nxt)
    {
        int to = ed[i].to;
        if (cut[i]) continue;
        Pushup(to, root, 1);
    }
    for (reg int i = head[root] ; i ; i = ed[i].nxt)
    {
        int to = ed[i].to;
        if (cut[i]) continue;
        cut[i] = cut[i ^ 1] = 1;
        solve(to);
    }
}

int main()
{
    n = read(), k = read();
    for (reg int i = 1 ; i < n ; i ++)
    {
        int x = read(), y = read(), z = read();
        add(x + 1, y + 1, z), add(y + 1, x + 1, z);
    }
    memset(len, 0x3f, sizeof len);
    solve(1);
    if (ans == 1e9) ans = -1;
    printf("%d\n", ans);
    return 0;
}
 
 
 

猜你喜欢

转载自www.cnblogs.com/BriMon/p/9478093.html