CodeForces 1004E Sonya and Ice Cream 贪心+树的直径+二分

E. Sonya and Ice Cream
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

Sonya likes ice cream very much. She eats it even during programming competitions. That is why the girl decided that she wants to open her own ice cream shops.

Sonya lives in a city with nn junctions and n1n−1 streets between them. All streets are two-way and connect two junctions. It is possible to travel from any junction to any other using one or more streets. City Hall allows opening shops only on junctions. The girl cannot open shops in the middle of streets.

Sonya has exactly kk friends whom she can trust. If she opens a shop, one of her friends has to work there and not to allow anybody to eat an ice cream not paying for it. Since Sonya does not want to skip an important competition, she will not work in shops personally.

Sonya wants all her ice cream shops to form a simple path of the length rr (1rk1≤r≤k), i.e. to be located in different junctions f1,f2,,frf1,f2,…,fr and there is street between fifi and fi+1fi+1 for each ii from 11 to r1r−1.

The girl takes care of potential buyers, so she also wants to minimize the maximum distance between the junctions to the nearest ice cream shop. The distance between two junctions aa and bb is equal to the sum of all the street lengths that you need to pass to get from the junction aa to the junction bb. So Sonya wants to minimize

maxamin1irda,fimaxamin1≤i≤rda,fi

where aa takes a value of all possible nn junctions, fifi — the junction where the ii-th Sonya's shop is located, and dx,ydx,y — the distance between the junctions xx and yy.

Sonya is not sure that she can find the optimal shops locations, that is why she is asking you to help her to open not more than kk shops that will form a simple path and the maximum distance between any junction and the nearest shop would be minimal.

Input

The first line contains two integers nn and kk (1kn1051≤k≤n≤105) — the number of junctions and friends respectively.

Each of the next n1n−1 lines contains three integers uiuivivi, and didi (1ui,vin1≤ui,vi≤nviuivi≠ui1d1041≤d≤104) — junctions that are connected by a street and the length of this street. It is guaranteed that each pair of junctions is connected by at most one street. It is guaranteed that you can get from any junctions to any other.

Output

Print one number — the minimal possible maximum distance that you need to pass to get from any junction to the nearest ice cream shop. Sonya's shops must form a simple path and the number of shops must be at most kk.

Examples
input
Copy
6 2
1 2 3
2 3 4
4 5 2
4 6 3
2 4 6
output
Copy
4
input
Copy
10 3
1 2 5
5 7 2
3 2 6
10 6 3
3 8 1
6 4 2
4 1 6
6 9 4
5 2 5
output
Copy
7
Note

In the first example, you can choose the path 2-4, so the answer will be 4.

The first example.

In the second example, you can choose the path 4-1-2, so the answer will be 7.

The second example.

做这个题的第一个结论是,选中的所有商店肯定都在树的直径上。反证法可证明。

其次我们要对答案进行二分,然后贪心验证当前二分到的答案是否成立。在求树的直径时,我们使用略作改动的dp的求法(此时已经不能正确求出树的直径在其字数内的次长路径长,不过没有关系,所有直径上的点,我们只会用到它在子树内的最长路径),已经可以求出当把树的直径某一段点作为根节点时,某一点在其子树内的最长路径长和次长路径长。做贪心的时候,如果直径上点的数目大于k,直接看直径上每一点的次长路径长是否超过二分数值即可。如果k小于直径上点的个数,就贪心法找出第一个到根节点距离小于二分数值的点,从这个点往后找k个点,最后一个点到直径终点的距离同样不得超过二分的数值,每个点的次长路径长不得超过二分的数值。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string.h>
#include<cmath>
#include<vector>
using namespace std;

int n,k,e,head[100010],r[100005],f1[100005],f2[100005],len;
vector<int> vec;

struct Edge{
    int to,cost,next;
}edge[200010];

void addedge(int a,int b, int c)
{
    edge[e].to=b;
    edge[e].cost=c;
    edge[e].next=head[a];
    head[a]=e++;
}

void dfs1(int u, int fa)
{
    r[u]=u;
    for (int i=head[u]; i!=-1; i=edge[i].next)
    {
        int v=edge[i].to;
        if (v==fa) continue;
        dfs1(v,u);
        if (f1[u]<f1[v]+edge[i].cost)
        {
            f1[u]=f1[v]+edge[i].cost; r[u]=r[v];
        }
    }
}

void dfs2(int u, int fa)
{
    r[u]=u;
    for (int i=head[u]; i!=-1; i=edge[i].next)
    {
        int v=edge[i].to;
        if (v==fa) continue;
        //cout<<"v="<<v<<endl;
        dfs2(v,u);
        if (f1[u]<=f1[v]+edge[i].cost)
        {
            f2[u]=f1[u];
            f1[u]=f1[v]+edge[i].cost;
            r[u]=v;
        }else if (f2[u]<f1[v]+edge[i].cost)
        {
            f2[u]=f1[v]+edge[i].cost;
        }
        //cout<<"u="<<u<<" f1="<<f1[u]<<" f2="<<f2[u]<<endl;
    }
}

bool fun(int d)
{
    //cout<<"d="<<d<<endl;
    bool tag=false; int num=0;
    for (int i=0; i<vec.size(); i++)
    {
        //cout<<"i="<<vec[i]<<" f1="<<f1[vec[i]]<<" len="<<len<<endl;
        if (!tag)
        {
            if (len-f1[vec[i]]<=d) continue;
            tag=true; num=2;
            if (k==1)
            {
                if (f1[vec[i-1]]>d || f2[vec[i-1]]>d) return false;
                return true;
            }else
            {
                if (f2[vec[i]]>d || f2[vec[i-1]]>d) return false;
                //cout<<"yes"<<endl;
                if (k==2 && f1[vec[i]]>d) return false;
                if (k==2) return true;
            }
        }else
        {
            num++;
            if (f2[vec[i]]>d) return false;
            if (num==k)
            {
                if (f1[vec[i]]>d) return false;
                return true;
            }
        }
    }
    return true;
}

long long solve()
{
    long long l=0; long long r=1e9;
    long long ans=-1;
    while (l<=r)
    {
        long long mid=(l+r)/2;
        if (fun(mid))
        {
            r=mid-1; ans=mid;
        }else
        {
            l=mid+1;
        }
    }
    return ans;
}

int main()
{
    scanf("%d%d",&n,&k);
    memset(head,-1,sizeof(head));
    e=0;
    for (int i=1; i<n; i++)
    {
        int a,b,c; scanf("%d%d%d",&a,&b,&c);
        addedge(a,b,c); addedge(b,a,c);
    }
    memset(f1,0,sizeof(f1));
    memset(f2,0,sizeof(f2));
    dfs1(1,0);
    int tmp=r[1];
    //cout<<"tmp="<<tmp<<endl;
    memset(f1,0,sizeof(f1));
    dfs2(tmp,0);
    len=f1[tmp];
    vec.push_back(tmp);
    while (tmp!=r[tmp])
    {
        tmp=r[tmp];
        vec.push_back(tmp);
    }
    if (k>vec.size())
    {
        int ans=0;
        for (int i=0; i<vec.size(); i++)
        {
            ans=max(ans,f2[vec[i]]);
        }
        cout<<ans<<endl;
        return 0;
    }
    cout<<solve();
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值