bzoj2599: [IOI2011]Race

3 篇文章 0 订阅

Description
给一棵树,每条边有权.求一条简单路径,权值和等于K,且边的数量最小.N <= 200000, K <= 1000000

Input
第一行 两个整数 n, k
第二..n行 每行三个整数 表示一条无向边的两端和权值 (注意点的编号从0开始)

Output
一个整数 表示最小边数量 如果不存在这样的路径 输出-1

Sample Input
4 3
0 1 1
1 2 2
1 3 4

Sample Output
2

颓废了一波的我重新开始更blog了..

这道题比较显然的就是用点分治啊.. 找重心然后再找经过它的所有路径咯..

时间复杂度就是 O(nlogn) ..

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int Maxn = 200010;
const int Maxk = 1000010;
const int inf = 0x7fffffff;
struct node {
    int x, y, next, d;
}a[Maxn*2]; int first[Maxn], len;
void ins ( int x, int y, int d ){
    len ++;
    a[len].x = x; a[len].y = y; a[len].d = d;
    a[len].next = first[x]; first[x] = len;
}
int _min ( int x, int y ){ return x < y ? x : y; }
int n, K, cur, s[Maxn], d[Maxn], f[Maxn], core;
int alls, ans;
int v[Maxk], cnt[Maxk];
bool done[Maxn]; int dep[Maxn];
void dfs ( int x, int fa ){
    s[x] = 1; f[x] = 0;
    for ( int k = first[x]; k; k = a[k].next ){
        int y = a[k].y;
        if ( y == fa || done[y] == true ) continue;
        dfs ( y, x );
        s[x] += s[y];
        if ( s[y] > f[x] ) f[x] = s[y];
    }
    if ( alls-s[x] > f[x] ) f[x] = alls-s[x];
    if ( f[core] > f[x] ) core = x;
}
void calc ( int x, int fa ){
    if ( d[x] > K ) return;
    if ( v[K-d[x]] == cur ) ans = _min ( ans, cnt[K-d[x]]+dep[x] );
    for ( int k = first[x]; k; k = a[k].next ){
        int y = a[k].y;
        if ( y == fa || done[y] == true ) continue;
        d[y] = d[x]+a[k].d;
        dep[y] = dep[x]+1;
        calc ( y, x );
    }
}
void update ( int x, int fa ){
    if ( d[x] > K ) return;
    if ( v[d[x]] != cur ){
        v[d[x]] = cur;
        cnt[d[x]] = dep[x];
    }
    else cnt[d[x]] = _min ( cnt[d[x]], dep[x] );
    for ( int k = first[x]; k; k = a[k].next ){
        int y = a[k].y;
        if ( y == fa || done[y] == true ) continue;
        update ( y, x );
    }
}
void solve ( int x ){
    done[x] = true;
    cur = x;
    v[0] = cur;
    cnt[0] = 0;
    for ( int k = first[x]; k; k = a[k].next ){
        int y = a[k].y;
        if ( done[y] == true ) continue;
        d[y] = a[k].d;
        dep[y] = 1;
        calc ( y, x );
        update ( y, x );
    }
    for ( int k = first[x]; k; k = a[k].next ){
        int y = a[k].y;
        if ( done[y] == true ) continue;
        core = 0;
        alls = f[0] = s[y];
        dfs ( y, 0 );
        solve (core);
    }
}
int main (){
    int i, j, k;
    scanf ( "%d%d", &n, &K );
    for ( i = 1; i < n; i ++ ){
        int x, y, dd;
        scanf ( "%d%d%d", &x, &y, &dd ); x ++; y ++;
        ins ( x, y, dd ); ins ( y, x, dd );
    }
    ans = inf;
    core = 0;
    alls = f[0] = n;
    dfs ( 1, 0 );
    memset ( done, false, sizeof (done) );
    solve (core);
    if ( ans == inf ) printf ( "-1\n" );
    else printf ( "%d\n", ans );
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值