NOIP2015Day2T3-运输计划


题解:
二分答案。
假设枚举到答案是 x x x
找出所有长度大于 x x x的链,那么这些链上必须要放虫洞,而且必须放在一条所有链都经过的边上,否则必然有一条链的长度是大于 x x x的。
这个直接差分就能求出所有链都经过的边,并且把最长的那条改为虫洞,看看所有边是否合法,则可判断 x x x是否可行。
应该不是很难吧。。。
比较简单的NOIP题。
不过代码有点毒瘤,95分了n遍,最后把swap改掉才过。
Code:

#include<bits/stdc++.h>
#define ll long long
const int N=1e6+5;
using namespace std;
int dist[N],len[N],head[N],n,m,deep[N],fa[N];
int tot,t[N],f[N][20],A[N],B[N],ans[N],sum[N];
inline ll read()
{
    ll x=0,f=1;char s=getchar();
    while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
    while(s<='9'&&s>='0'){x=x*10+s-'0';s=getchar();}
    return x*f;
}
struct node
{
    int vet,next,len;
}edge[N];
void add(int u,int v,int w)
{
    edge[++tot].vet=v;
    edge[tot].next=head[u];
    head[u]=tot;
    edge[tot].len=w;
}
void dfs(int u)
{
    for(int i=head[u];i;i=edge[i].next)
    {
        int v=edge[i].vet;
        if(v!=fa[u])
        {
            deep[v]=deep[u]+1;
    		fa[v]=u;
        	len[v]=edge[i].len;
            dist[v]=dist[u]+len[v];
            dfs(v);
        }
    }
}
int lca(int u,int v)
{
    if(deep[u]<deep[v])u^=v^=u^=v;
    for(int j=19;j>=0;j--)
        if(deep[f[u][j]]>=deep[v])u=f[u][j];
    if(u==v)return u;
    for(int j=19;j>=0;j--)
        if(f[u][j]!=f[v][j])u=f[u][j],v=f[v][j];
    return fa[u];
}
void Dfs(int u)
{	for(int i=head[u];i;i=edge[i].next)
    {
        int v=edge[i].vet;
        if(v!=fa[u])
        {
    		Dfs(v);
        	sum[u]+=sum[v];
        }
    }
}
int check(int x)
{
    int cnt=0,mx=0;
    for(int i=1;i<=n;i++)sum[i]=0;
    for(int i=1;i<=m;i++)
        if(ans[i]>x)
        {
            cnt++;
            mx=max(mx,ans[i]-x);
            sum[A[i]]++;
            sum[B[i]]++;
            sum[t[i]]-=2;
        }
    Dfs(1);
    for(int i=1;i<=n;i++)
        if(sum[i]==cnt&&len[i]>=mx)return 1;
    return 0;
}
int main()
{
    n=read(),m=read();	
    for(int i=1;i<n;i++)
    {
        int u=read(),v=read(),w=read();
        add(u,v,w);add(v,u,w);
    }
    dfs(1);
    for(int i=1;i<=n;i++)f[i][0]=fa[i];
    for(int j=1;j<20;j++)
        for(int i=1;i<=n;i++)
            f[i][j]=f[f[i][j-1]][j-1];
    int l=0,r=0;
    for(int i=1;i<=m;i++)
    {
        A[i]=read(),B[i]=read();
        int u=A[i],v=B[i];
        t[i]=lca(u,v);
        ans[i]=dist[u]+dist[v]-2*dist[t[i]];
        r=max(r,ans[i]);
    }
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(check(mid))r=mid-1;else l=mid+1;
    }
    printf("%d\n",l);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JackflyDC

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值