[P2680][NOIP2015]运输计划

6 篇文章 0 订阅
3 篇文章 0 订阅

原题链接

二分+树上差分

T了一个点
就5分不想了

二分一个最大长度
超过这个长度的路线
找它们的交

Tarjan找LCA
u,v+1
LCA-2

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<climits>
#include<cstdlib>
#include<ctime>
#include<cstring>
#include<queue>
#define LL long long
using namespace std;

int cnt1,cnt2,v1[300005],v2[300005],num1[300005],num2[300005],nxt1[300005],nxt2[300005],head1[300005],head2[300005];
int c[300005],ques[300005],fa[300005],dt[300005],dep[300005],dis[300005],n,m,u[300005],v[300005],f[300005];
bool vis[300005];

void add1(int p1,int p2,int val)
{
    cnt1++;
    v1[cnt1]=val;
    num1[cnt1]=p2;
    nxt1[cnt1]=head1[p1];
    head1[p1]=cnt1; 
}

void add2(int p1,int p2,int val)
{
    cnt2++;
    v2[cnt2]=val;
    num2[cnt2]=p2;
    nxt2[cnt2]=head2[p1];
    head2[p1]=cnt2; 
}

int find(int x)
{
    if(f[x]==x) return x;

    f[x]=find(f[x]);
    return f[x];
}

void tarjan(int x)
{
    int i;

    vis[x]=1;

    for(i=head1[x];i;i=nxt1[i])
        if(!vis[num1[i]])
        {
            tarjan(num1[i]);        
            f[num1[i]]=x;
        }

    for(i=head2[x];i;i=nxt2[i])
        if(vis[num2[i]])
            ques[v2[i]]=find(num2[i]);
}

void DFS(int x,int pre)
{
    int i;

    for(i=head1[x];i;i=nxt1[i]) 
        if(num1[i]!=pre) 
        {
            DFS(num1[i],x);
            c[x]+=c[num1[i]];
            fa[num1[i]]=v1[i];  
        }
}

bool check(int x)
{
    int i,maxn=0,tot=0,maxdis;

    memset(c,0,sizeof(c));

    for(i=1;i<=m;i++)
        if(dt[i]>x)
        {
            c[u[i]]++;
            c[v[i]]++;
            c[ques[i]]-=2;  
            tot++;
            maxn=max(maxn,dt[i]);
        }   

    DFS(1,0);
    maxdis=maxn;

    for(i=1;i<=n;i++)
        if(c[i]==tot)
            maxdis=min(maxdis,maxn-fa[i]);

    if(maxdis>x) return 0;
    return 1;
}

void dfs(int x,int pre)
{
    int i;

    dep[x]=dep[pre]+1;

    for(i=head1[x];i;i=nxt1[i])
        if(num1[i]!=pre) 
        {
            dis[num1[i]]=dis[x]+v1[i];
            dfs(num1[i],x); 
        }
}

int main()
{
    int i,l=0,r=0,mid,a,b,t,ans;

    scanf("%d%d",&n,&m);

    for(i=1;i<=n;i++)
        f[i]=i;

    for(i=1;i<n;i++)
    {
        scanf("%d%d%d",&a,&b,&t);
        add1(a,b,t);
        add1(b,a,t);
        r+=t;
    }

    dfs(1,0);

    for(i=1;i<=m;i++)
    {
        scanf("%d%d",&u[i],&v[i]);
        add2(u[i],v[i],i);  
        add2(v[i],u[i],i);
    }   

    tarjan(1);  

    for(i=1;i<=m;i++)
        dt[i]=dis[u[i]]+dis[v[i]]-2*dis[ques[i]];       

    while(l<=r)
    {
        mid=(l+r)>>1;

        if(check(mid)) r=mid-1,ans=mid;
        else l=mid+1;
    }

    printf("%d",ans);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值