P2680 运输计划[二分+LCA+树上差分]

题目描述

公元20442044 年,人类进入了宇宙纪元。

L 国有 nn 个星球,还有 n-1n?1 条双向航道,每条航道建立在两个星球之间,这 n-1n?1 条航道连通了 LL 国的所有星球。

小 P 掌管一家物流公司, 该公司有很多个运输计划,每个运输计划形如:有一艘物流飞船需要从 u_i*u**i* 号星球沿最快的宇航路径飞行到 v_i*v**i* 号星球去。显然,飞船驶过一条航道是需要时间的,对于航道 jj,任意飞船驶过它所花费的时间为 t_j*t**j*,并且任意两艘飞船之间不会产生任何干扰。

为了鼓励科技创新, LL 国国王同意小 PP 的物流公司参与 LL 国的航道建设,即允许小PP 把某一条航道改造成虫洞,飞船驶过虫洞不消耗时间。

在虫洞的建设完成前小 P 的物流公司就预接了 mm 个运输计划。在虫洞建设完成后,这 mm 个运输计划会同时开始,所有飞船一起出发。当这 mm 个运输计划都完成时,小 PP 的物流公司的阶段性工作就完成了。

如果小 PP 可以自由选择将哪一条航道改造成虫洞, 试求出小 PP 的物流公司完成阶段性工作所需要的最短时间是多少?

解析

这题还是算比较简单的,暴力也能打满分。暴力一点的,树剖一条条试着删m条路径中的最长路径上的边就完事了。

题意很简单:给定带权树,将树中某条边的权值替换为0,并使替换后的最长路径最短。

看到最小化最大值,首先想到二分答案。首先我们要做一些基本的事情,这种在树上给定一对对起点终点的题目,肯定每对起点终点对应的路径是唯一的,我们先求出这些路径,即求LCA。

现在考虑如何二分,现在我们要解决的问题肯定就是在替换哪一条边、能不能替换的问题上了。显然,如果要使得所有起点终点对应的路径长度小于二分出的值,那么我们只需考虑那些比二分出的值大的路径。我们来考察这些路径,发现由于我们只能替换一条边,那么这条要替换的边就一定得是所有比二分出的值大的路径的公共边,否则至少要替换两条边才能使得当前二分情况成立。

那么如何判断所有比二分出的值大的路径有公共边呢?这里是难点。不难想出解决办法,就是统计所有比二分出的值大的路径的经过的边,做路径覆盖,如果最后某一条边被覆盖的次数与比二分出的值大的路径条数相等的话,这条边就是公共边。为了加快计算速度,我们可以使用树上差分技巧。

又如何判断当前二分情况是否成立呢?首先,如果我们没有找到公共边,那么当前情况一定不成立,其至少需要替换两条边。如果我们找到了一条以上的公共边,贪心的思想,一定是替换这些公共边中最长的那一条使得最长路径最短,而如果我们替换掉这最长的一条后最长路径仍然不满足当前二分出来的值,就不合法。

分析到这里,我们发现我们需要以下信息:

  • 所有起点和终点对应的最长的路径长度
  • 所有起点和终点对应的路径长度
  • 每对起点和终点的LCA
  • 二分时,树上每条边被覆盖的次数

这题也没什么好注意的细节问题,二分写好了就没事。

参考代码

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#define N 300010
#define ri register int
using namespace std;
inline int read()
{
    int f=1,x=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}
int n,m,f[31][N],t,dep[N],d[N],c[N];
struct node{
    int u,v,lca,dis;
}s[N];
struct rec{
    int next,ver,edge;
}g[N<<1];
int head[N],tot,up[N];
inline void add(int x,int y,int val)
{
    g[++tot].ver=y,g[tot].edge=val;
    g[tot].next=head[x],head[x]=tot;
}
inline void init()
{
    queue<int> q;
    q.push(1);dep[1]=1;
    while(q.size()){
        int x=q.front();q.pop();
        for(ri i=head[x];i;i=g[i].next){
            int y=g[i].ver,z=g[i].edge;
            if(dep[y]) continue;
            dep[y]=dep[x]+1;
            d[y]=d[x]+z,up[y]=z;
            f[0][y]=x;
            for(ri j=1;j<t;++j)
                f[j][y]=f[j-1][f[j-1][y]];
            q.push(y);
        }
    }
}
inline int lca(int x,int y)
{
    if(dep[y]>dep[x]) swap(x,y);
    for(int j=t;j>=0;--j)
        if(dep[f[j][x]]>=dep[y]) x=f[j][x];
    if(x==y) return x;
    for(int j=t;j>=0;--j)
        if(f[j][x]!=f[j][y]) x=f[j][x],y=f[j][y];
    return f[0][x];
}
inline void dfs(int x,int fa)
{
    for(ri i=head[x];i;i=g[i].next){
        int y=g[i].ver;
        if(y==fa) continue;
        dfs(y,x);
        c[x]+=c[y];
    }
}
inline bool check(int x)
{
    int maxx=0,maxn=0,cnt=0;
    memset(c,0,sizeof(c));
    for(ri i=1;i<=m;++i)
        if(s[i].dis>x){
            cnt++;
            c[s[i].u]++,c[s[i].v]++;
            c[s[i].lca]-=2;
            maxx=max(maxx,s[i].dis);
        }
    if(!cnt) return 1;
    dfs(1,-1);
    for(ri i=1;i<=n;++i)
        if(c[i]==cnt) maxn=max(maxn,up[i]);
    return maxx-maxn<=x;
}
int main()
{
    int maxx=0;
    n=read(),m=read();
    t=log2(n)+1;
    for(int i=1;i<n;++i){
        int u=read(),v=read(),val=read();
        add(u,v,val),add(v,u,val);
    }
    init();
    for(ri i=1;i<=m;++i){
        int f=read(),t=read();
        s[i].u=f,s[i].v=t;
        s[i].lca=lca(f,t);
        s[i].dis=d[f]+d[t]-d[s[i].lca]*2;
        maxx=max(maxx,s[i].dis);
    }
    int l=0,r=maxx;
    while(l<r){
        int mid=(l+r)>>1;
        if(check(mid)) r=mid;
        else l=mid+1;
    }
    printf("%d\n",l);
    return 0;
}

原文地址:https://www.cnblogs.com/DarkValkyrie/p/11624743.html

时间: 2024-10-07 08:58:45

P2680 运输计划[二分+LCA+树上差分]的相关文章

[luogu]P2680 运输计划

原题链接 :P2680 运输计划 分析 题意很简单,给定一张连通图,n个点,n-1条边,很显然是一棵树. 现在给定m条链\((u,v)\). 现在可以cut掉一条边(边权置为0). 现在求最长链的最小值. 55pts 蒟蒻的我肯定是拿不到满分的..我们直接考虑部分分. 对于m=1的点,我们只需要枚举链上的全部边,然后cut掉最长的那一个就可以了. 裸搜一遍dfs,然后求出链长,最后减去最长边就可以了. 然后是第i条航线链接i和i+1点的点. 很明显整张图形成了一条链. 前缀和维护整张图,这样子可

NOIp2015 运输计划 [LCA] [树上差分] [二分答案]

我太懒了 吃掉了题面 题解 & 吐槽 一道很好的树上差分练习题. 不加fread勉强a过bzoj和luogu的数据,加了fread才能在uoj里卡过去. 可以发现,答案则是运输计划里花费的最大值,最大值最小,便是二分答案的标志. 那么该怎么check呢... 我们得找出所有超过限制的计划,这个过程可以在LCA倍增的过程中预处理出来. 然后再找出一些被这些计划都覆盖的边,找到最大的那条边,如果最大的计划花费减去最大的那条边小于x,那么x就是可行的. 但是该怎么找到那些被计划都覆盖的边呢... 我们

BZOJ 4326 NOIP2015 运输计划 (二分+树上差分)

题意:中文题. 析:首先二分是很容易想出来的,然后主要是判断这个解合不合法,先二分答案 mid,因为有 m 个计划,所以只要添加虫洞的肯定是所有的时间长于 mid 的计划 中,也就是是那些的共同边,这个就可以用树上差分来做了,假设 s 到 t,那么让in[s]++,in[t]++,in[lca(s, t)] -= 2,其中in 表示的是 该结点与其父结点的边的计数,最后再跑一次dfs,把所有的权值都累加上去,这样就能知道哪些是共同的边了. 代码如下: #pragma comment(linker

P2680 运输计划

题目背景 公元 2044 年,人类进入了宇宙纪元. 题目描述 L 国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星球之间,这 n-1 条航道连通了 L 国的所有星球. 小 P 掌管一家物流公司,该公司有很多个运输计划,每个运输计划形如:有一艘物 流飞船需要从 ui 号星球沿最快的宇航路径飞行到 vi 号星球去.显然,飞船驶过一条航道 是需要时间的,对于航道 j,任意飞船驶过它所花费的时间为 tj,并且任意两艘飞船之 间不会产生任何干扰. 为了鼓励科技创新,L 国国王同意小 P 的

Vijos[1983]NOIP2015Day2T3 运输计划 transport LCA

题目链接Vijos 题目链接UOJ 转载一个大佬的题解: 点击这里->银牌爷题解 主要考察二分查找.树上倍增.贪心."树上前缀和".题目是一颗树,要求将一条边的权值变为0,使得所有运输计划的最大时间最小.直觉告诉我们,这是一个树上倍增的题目,但是它却不像前几年的 Day2T3 开车旅行那样纯倍增,或许更像疫情控制一些,倍增只是辅助算法,还需要配合其他算法.由于要使所有运输计划的最大时间最小,不难想到二分答案的方法.使C(t)表示是否可以改造一条边,使得改造之后所有运输计划中最长的

洛谷——P2680 运输计划

https://www.luogu.org/problem/show?pid=2680 题目背景 公元 2044 年,人类进入了宇宙纪元. 题目描述 L 国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星球之间,这 n-1 条航道连通了 L 国的所有星球. 小 P 掌管一家物流公司,该公司有很多个运输计划,每个运输计划形如:有一艘物 流飞船需要从 ui 号星球沿最快的宇航路径飞行到 vi 号星球去.显然,飞船驶过一条航道 是需要时间的,对于航道 j,任意飞船驶过它所花费的时间为 t

[NOIP2015] 提高组 洛谷P2680 运输计划

题目背景 公元 2044 年,人类进入了宇宙纪元. 题目描述 L 国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星球之间,这 n-1 条航道连通了 L 国的所有星球. 小 P 掌管一家物流公司,该公司有很多个运输计划,每个运输计划形如:有一艘物 流飞船需要从 ui 号星球沿最快的宇航路径飞行到 vi 号星球去.显然,飞船驶过一条航道 是需要时间的,对于航道 j,任意飞船驶过它所花费的时间为 tj,并且任意两艘飞船之 间不会产生任何干扰. 为了鼓励科技创新,L 国国王同意小 P 的

【洛谷】【lca+树上差分】P3258 [JLOI2014]松鼠的新家

[题目描述:] 松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n(2 ≤ n ≤ 300000)个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的.天哪,他居然真的住在”树“上. 松鼠想邀请小熊维尼前来参观,并且还指定一份参观指南,他希望维尼能够按照他的指南顺序,先去a1,再去a2,......,最后到an,去参观新家.可是这样会导致维尼重复走很多房间,懒惰的维尼不停地推辞.可是松鼠告诉他,每走到一个房间,他就可以从房间拿一块糖果吃. 维尼是个馋家伙,立马就

POJ - 3417 Network (LCA+树上差分)

题目传送门:POJ - 3417 Network 题目大意: 存在一棵n个结点的树,加入m条新边,现在要让这个图不连通,你可以切断两条边,要求切断一条原边,一条新边,求切割的方案数. 分析: 加入m条新边,假设加入新边(u,v),那么u-->lca(u,v)-->v-->u形成一个环,此时可以切断新边,和环上任意的一条原边就可以使图不连通. 可以发现若加入一条新边,给环上的计数1,表示该边被一个环覆盖,树上有些边会被多个环覆盖,此时可以分情况讨论. 1.若该边被覆盖次数是0,则断掉该边后