洛谷 P2680 运输计划 树链剖分+最近公共祖先

题目背景
公元2044年,人类进入了宇宙纪元。
题目描述
公元2044年,人类进入了宇宙纪元。
L L 国有n个星球,还有 n1 n − 1 条双向航道,每条航道建立在两个球之间,这 n1 n − 1 条航道连通了 L L 国的所有星球。
P掌管一家物流公司,该公司有很多个运输计划,每个运输计划形如:有一艘物流飞船需要从 ui u i 号星球沿最快的宇航路径飞行到 vi v i 号星球去。显然,飞船驶过一条航道是需要时间的,对于航道 j j ,任意飞船驶过它所花费的时间为tj,并且任意两艘飞船之间不会产生任何干扰。
为了鼓励科技创新, L L 国国王同意小P的物流公司参与 L L 国的航道建设,即允许小P把某一条航道改造成虫洞,飞船驶过虫洞不消耗时间。
在虫洞的建设完成前小 P P 的物流公司就预接了m个运输计划。在虫洞建设完成后,这 m m 个运输计划会同时开始,所有飞船一起出发。当这m个运输计划都完成时,小 P P 的物流公司的阶段性工作就完成了。
如果小P可以自由选择将哪一条航道改造成虫洞,试求出小 P P 的物流公司完成阶段性工作所需要的最短时间是多少?
输入输出格式
输入格式:

第一行包括两个正整数n,m,表示 L 国中星球的数量及小 P P 公司预接的运输计划的数量,星球从1 n n 编号。

接下来n1行描述航道的建设情况,其中第 i i 行包含三个整数 ai,bi ti t i ,表示第 i i 条双向航道修建在ai bi b i 两个星球之间,任意飞船驶过它所花费的时间为 ti t i 。数据保证
1ai,bin 1 ≤ a i , b i ≤ n 0ti1000 0 ≤ t i ≤ 1000

接下来 m m 行描述运输计划的情况,其中第j行包含两个正整数 uj u j vj v j ,表示第 j j 个运输计划是从uj号星球飞往 vj v j 号星球。
数据保证
1ui,vin 1 ≤ u i , v i ≤ n

输出格式:

一个整数,表示小 P P 的物流公司完成阶段性工作所需要的最短时间。

输入输出样例
输入样例#1:
6 3
1 2 3
1 6 4
3 1 7
4 3 6
3 5 5
3 6
2 5
4 5
输出样例#1:
11
说明
所有测试数据的范围和特点如下表所示
这里写图片描述
请注意常数因子带来的程序效率上的影响。

分析:先用倍增lca求出两点之间的距离。然后把距离从大到小排序,枚举位置 i i 表示前i条路径使用虫洞,后面的不使用虫洞的时间来更新答案,即 ans=min(ans,max(len1w,leni+1)) a n s = m i n ( a n s , m a x ( l e n 1 − w , l e n i + 1 ) ) 。而 w w 为前i条路径都共用的边的权值的最大值。我们可以用线段树维护区间边出现的次数最大值,当一条边出现次数等于 i i 时,维护他的权值最大值。这个可以使用链剖解决。因为w单调减,所以 len1w l e n 1 − w 单调加,所以当 len1w>=ans l e n 1 − w >= a n s 即可退掉。

代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>

const int maxn=3e5+7;

using namespace std;

struct node{
    int data,sum;
    int lazy;
}t[maxn*4];

struct edge{
    int y,w,next;
}g[maxn*2];

int n,m,x,y,w,cnt;
int f[maxn][20],dep[maxn],dfn[maxn],top[maxn],dis[maxn],size[maxn],ls[maxn],val[maxn],b[maxn];

struct rec{
    int u,v,l;
}a[maxn];

bool cmp(rec x,rec y)
{
    return x.l>y.l;
}

void add(int x,int y,int w)
{
    g[++cnt]=(edge){y,w,ls[x]};
    ls[x]=cnt;
}

void dfs1(int x,int fa)
{
    size[x]=1;
    f[x][0]=fa;
    for (int i=ls[x];i>0;i=g[i].next)
    {
        int y=g[i].y;
        if (y==fa) continue;
        dis[y]=dis[x]+g[i].w;
        dep[y]=dep[x]+1;
        val[y]=g[i].w;
        dfs1(y,x);
        size[y]+=size[x];
    }
}

void dfs2(int x,int d)
{
    top[x]=d;
    dfn[x]=++cnt;
    int c=0;
    for (int i=ls[x];i>0;i=g[i].next)
    {
        int y=g[i].y;
        if (f[x][0]==y) continue;
        if (size[y]>size[c]) c=y;
    }
    if (!c) return;
    dfs2(c,d);
    for (int i=ls[x];i>0;i=g[i].next)
    {
        int y=g[i].y;
        if ((f[x][0]==y) || (y==c)) continue;
        dfs2(y,y);
    }
}

int lca(int x,int y)
{
    if (dep[x]>dep[y]) swap(x,y);
    int d=dep[y]-dep[x],k=19,t=1<<k;
    while (d)
    {
        if (d>=t) y=f[y][k],d-=t;
        t/=2; k--;
    }
    if (x==y) return x;
    k=19;
    while (k>=0)
    {
        if (f[x][k]!=f[y][k])
        {
            x=f[x][k];
            y=f[y][k];
        }
        k--;
    }
    return f[x][0];
}

void build(int p,int l,int r)
{
    if (l==r)
    {
        t[p].data=b[l];
        return;
    }
    int mid=(l+r)/2;
    build(p*2,l,mid);
    build(p*2+1,mid+1,r);
    t[p].data=max(t[p*2].data,t[p*2+1].data);
}

void ins(int p,int l,int r,int x,int y,int k)
{
    if ((l==x) && (r==y))
    {
        t[p].sum+=1;
        t[p].lazy+=1;
        return;
    }
    int mid=(l+r)/2;
    if (t[p].lazy)
    {
        t[p*2].lazy+=t[p].lazy;
        t[p*2+1].lazy+=t[p].lazy;
        t[p*2].sum+=t[p].lazy;
        t[p*2+1].sum+=t[p].lazy;
        t[p].lazy=0;
    }
    if (y<=mid) ins(p*2,l,mid,x,y,k);
    else if (x>mid) ins(p*2+1,mid+1,r,x,y,k);
    else
    {
        ins(p*2,l,mid,x,mid,k);
        ins(p*2+1,mid+1,r,mid+1,y,k);
    }
    t[p].sum=max(t[p*2].sum,t[p*2+1].sum);
    t[p].data=0;
    if (t[p*2].sum==k) t[p].data=max(t[p].data,t[p*2].data);
    if (t[p*2+1].sum==k) t[p].data=max(t[p].data,t[p*2+1].data);
}

void change(int x,int y,int k)
{
    while (top[x]!=top[y])
    {
        if (dep[top[x]]>dep[top[y]]) swap(x,y);
        ins(1,1,n,dfn[top[y]],dfn[y],k);
        y=f[top[y]][0];
    }
    if (dep[x]>dep[y]) swap(x,y);
    if (x!=y) ins(1,1,n,dfn[x]+1,dfn[y],k);
}

int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<n;i++)
    {
        scanf("%d%d%d",&x,&y,&w);
        add(x,y,w);
        add(y,x,w);
    }
    dfs1(1,0);
    cnt=0;
    dfs2(1,0);
    for (int j=1;j<20;j++)
    {
        for (int i=1;i<=n;i++)
        {
            f[i][j]=f[f[i][j-1]][j-1];
        }
    }

    for (int i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        a[i].l=dis[x]+dis[y]-2*dis[lca(x,y)];
        a[i].u=x;
        a[i].v=y;
    }
    sort(a+1,a+m+1,cmp);
    for (int i=1;i<=n;i++) b[dfn[i]]=val[i];
    build(1,1,n);
    int ans=a[1].l;
    for (int i=1;i<=m;i++)
    {
        change(a[i].u,a[i].v,i);
        int d=t[1].data;
        if (a[1].l-d>=ans) break;
        ans=min(ans,max(a[1].l-d,a[i+1].l));
    }
    printf("%d\n",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值