运输计划

42 篇文章 0 订阅
39 篇文章 0 订阅

题目链接

暴力分:

贪心20,不写了。

正解:二分

二分最短的时间。
通过预处理求出每条航线的总时间。
二分一个时间。
是否存在一条边,减下它的时间让所有的大于mid的路,全部小于mid。

如何找一条被重复经过的边?

树上差分。
对于数组v
V[起点,终点]+1,V[lca(起点,终点)]-=2
从i到他的父节点的路径被经过的次数即为i子树v的和。

注意要用Tarjan,不能用倍增(被卡了一个点)233

#include <iostream>
#include <cstdio>
#include <cstring> 
using namespace std;
const int maxm=610000;
int head[2][maxm],to[2][2*maxm],cost[2][2*maxm],net[2][2*maxm],lca[2][2*maxm];
int fat[maxm],dis[maxm],cap[maxm];
int v[maxm];
bool vis[maxm];
int cnt[2];
int n,m;
struct node{
    int s,t,dis;
}a[maxm];
void add(int id,int x,int y,int c)
{
    cnt[id]++;
    to[id][cnt[id]]=y;
    cost[id][cnt[id]]=c;
    net[id][cnt[id]]=head[id][x];
    head[id][x]=cnt[id];
}
int find(int x)
{
    if(fat[x]==x) return x;
    else return fat[x]=find(fat[x]);
}
void tarjan(int x,int f,int Dis)
{
    vis[x]=1; 
    dis[x]+=dis[f]+Dis;
    fat[x]=x;
    for(int i=head[0][x];i;i=net[0][i])
    if(to[0][i]!=f)
    {
        tarjan(to[0][i],x,cost[0][i]);
        fat[to[0][i]]=x;
        cap[to[0][i]]=cost[0][i];
    }
    for(int i=head[1][x];i;i=net[1][i])
    if(vis[to[1][i]])
    {
        lca[1][i]=find(to[1][i]);
        if(i%2)
         lca[1][i+1]=lca[1][i];
        else
         lca[1][i-1]=lca[1][i];
    } 
}
int dfs(int x,int fat)
{
    for(int i=head[0][x];i;i=net[0][i])
     if(to[0][i]!=fat)
      dfs(to[0][i],x),v[x]+=v[to[0][i]];
    return v[x];
}
bool check(int mid)
{
    memset(v,0,sizeof(v));
    int cnt=0,max_d=0;
    for(int i=1;i<=m;i++)
    if(a[i].dis>mid)
    {
        //printf("%d\n",a[i].dis);
        v[a[i].s]+=1;
        v[a[i].t]+=1;
        v[lca[1][2*i]]-=2;
        max_d=max(max_d,a[i].dis);
        cnt++;
    }
    dfs(1,0);
    /*printf("%d\n",cnt);
    for(int i=1;i<=n;i++)
     printf("%d ",v[i]);*/
    for(int i=2;i<=n;i++)
     if(v[i]==cnt&&max_d-cap[i]<=mid)
      return 1;
    //puts("");
    return 0;
} 
int main()
{
    scanf("%d%d",&n,&m);

    for(int i=1,x,y,c;i<n;i++)
    {
        scanf("%d%d%d",&x,&y,&c);
        add(0,x,y,c);
        add(0,y,x,c);
    }

    for(int i=1,x,y;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        a[i].s=x,a[i].t=y;
        add(1,x,y,0);
        add(1,y,x,0);
    }
    tarjan(1,0,0);
    int r=0;
    for(int i=1;i<=m;i++)
     a[i].dis=(dis[a[i].s]+dis[a[i].t]-2*dis[lca[1][2*i]]),r=max(r,a[i].dis);  
    int l=0;
    /*for(int i=1;i<=n;i++)
     printf("%d ",cap[i]);*/
    while(l<=r)
    {
        int mid=l+r>>1;
        if(check(mid)) r=mid-1;
        else l=mid+1;
    }
    printf("%d",l);
}    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值