bzoj 2599: [IOI2011]Race (点分治 本地过了就是过了.jpg)

题面:(复制别人的。。。)

Description

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

Input

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

Output

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

Sample Input

4 3
0 1 1
1 2 2
1 3 4

Sample Output

2
 
这道题好像是权限题。。bzoj上看不到这道题更别说交了。。。
写了半天结果交不了,样例过了就当过了吧。。QAQ
 
思路:
多建一个cnt数组表示权值为i的最小边长,得到每个点到根节点的边长和权值和然后逐个更新就好了。
 
实现代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int  M = 1e5 + 10;
int n,k,cnt1,root,f[M],head[M],siz[M],cnt[M],dis[M],vis[M],sum,ans;

struct node{
    int to,next,w;
}e[M];

void add(int u,int v,int w){
    e[++cnt1].to=v;e[cnt1].next=head[u];e[cnt1].w=w;head[u]=cnt1;
}

void get_root(int u,int fa){
    f[u] = 0; siz[u] = 1;
    for(int i = head[u];i;i=e[i].next){
        int v = e[i].to;
        if(v == fa||vis[v]) continue;
        get_root(v,u);
        siz[u] += siz[v];
        f[u] = max(f[u],siz[v]);
    }
    f[u] = max(sum-siz[u],f[u]);
    if(f[u] < f[root]) root = u;
}

void get_dis(int u,int fa,int dep){
    if(dis[u] >= 0&&dis[u] <= k) ans = min(ans,dep + cnt[k-dis[u]]);
    for(int i = head[u];i;i=e[i].next){
        int v = e[i].to;
        if(v==fa||vis[v]) continue;
        dis[v] = dis[u] + e[i].w;
        get_dis(v,u,dep+1);
    }
}

void cal(int u,int fa,int dep,int flag){
    if(dis[u] >= 0&&dis[u] <= k)  {
        if(flag) cnt[dis[u]] = min(dep,cnt[dis[u]]);
        else cnt[dis[u]] = n;
    }
    for(int i = head[u];i;i=e[i].next){
        int v = e[i].to;
        if(v==fa||vis[v]) continue;
        cal(v,u,dep+1,flag);
    }
}

void solve(int u){
    vis[u] = 1; cnt[0] = 0;
    for(int i = head[u];i;i=e[i].next){
        int v = e[i].to;
        if(vis[v]) continue;
        dis[v] = e[i].w;
        get_dis(v,0,1);
        cal(v,0,1,1);
    }
    for(int i = head[u];i;i=e[i].next){  //初始化
        int v = e[i].to;
        if(vis[v]) continue;
        cal(v,0,1,0);
    }
    for(int i = head[u];i;i = e[i].next){
        int v = e[i].to;
        if(vis[v]) continue;
        root = 0;
        sum = siz[v];
        get_root(v,0);
        solve(v);
    }
}

int main()
{
    int a,b,c;
    scanf("%d%d",&n,&k);
    ans = n;cnt1=1;
    for(int i = 1;i < n;i ++){
        scanf("%d%d%d",&a,&b,&c);
        a++;b++;
        add(a,b,c); add(b,a,c);
    }
    for(int i = 1;i <= k;i ++) cnt[i] = n;
    root = 0;f[0] = n; sum = n;get_root(1,0);
    solve(root);
    printf("%d\n",(ans==n)?-1:ans);
}

 

posted @ 2018-08-07 16:29  冥想选手  阅读(154)  评论(0编辑  收藏  举报