图论训练之四

https://www.luogu.org/problem/P2680

题意:n个点,n-1条边,边有边权,无向图,m条航线,0时刻同时启程,你可以将一个边权变为0,求使得最后一个航线到达最少花费时间。

这道题很早就做过了,但现在又忘了,

而且觉得这是一道好题,所以写一篇博客

分析;

首先最大值最小,二分毋庸置疑

当然是二分答案,但怎么判断就是本题的难点了

明确,删边一定是在最长的路线中删去(很好理解吧)

在此基础上

看能否有其他的路线经过删的边就更好

普及一下树上差分

如果是点差分,(a,b),则在a处+1,b处+1,LCA处-1,fa[LCA]处-1

如果是边差分,(a,b),则在a处+1,b处+1,LCA处-2

回到正题

关键在于check函数

check的时候把所有大于mid值的路径记录下来,找出被所有这样路径覆盖的最长的道路:

如果没有这样的道路 return false;

如果这样的道路被减去之后依然大于mid return false

找出被所有路径覆盖的道路:

在树中将所有路径起、始权值+1,LCA权值-2,从所有叶节点往上累加(dfs序维护真是好),

最终权值为路径数的点到其父亲的边为所求边 dis[i]表示i到根的距离;

tmp[i]表示i这个点通往父亲的边,目的是记录这条边被遍历的次数 ;

num[i]的作用是找到叶节点向上累加

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=300010;
struct node{
    int to,next,w;
}edge[maxn*2];
struct no{
    int u,v,lcaa,diss;
}lu[maxn*2];
int summ,cnt=0,k,n,m,num[maxn],mi[maxn],vis[maxn];
int temp[maxn],head[maxn],deep[maxn],dis[maxn];
int fa[maxn][25],dp[maxn][25];
void adde(int u,int v,int w){
    k++;
    edge[k].to=v;
    edge[k].next=head[u];
    edge[k].w=w;
    head[u]=k;
}
void dfs(int x,int pa,int dep){
    cnt++;
    num[cnt]=x;
    deep[x]=dep;
    vis[x]=1;
    for(int i=1;i<25;i++){
        fa[x][i]=fa[fa[x][i-1]][i-1];
    }
    for(int i=head[x];i>0;i=edge[i].next){
        int v=edge[i].to;
        if(!vis[v]){
            fa[v][0]=x;
            dis[v]=dis[x]+edge[i].w;
            dfs(v,x,dep+1);
        }
    }
}
int lca(int x,int y){
    if(deep[x]<deep[y]) swap(x,y);
    int t=deep[x]-deep[y];
    for(int i=0;i<25;i++){
        if((1<<i)&t) x=fa[x][i];
    }
    if(x==y) return x;
    for(int i=24;i>=0;i--){
        if(fa[x][i]!=fa[y][i]){
            x=fa[x][i];y=fa[y][i];
        }
    }
    return fa[x][0];
}
bool check(int mid){
    int cnt=0,ans=0;
    memset(temp,0,sizeof(temp));
    for(int i=1;i<=m;i++){
        if(lu[i].diss>mid){
            temp[lu[i].u]++;temp[lu[i].v]++;temp[lu[i].lcaa]-=2;
            ans=max(ans,lu[i].diss-mid);
            cnt++;
        }
    }
    if(cnt==0) return true;
    for(int i=n;i>=1;i--) temp[fa[num[i]][0]]+=temp[num[i]];
    for(int i=2;i<=n;i++) if(temp[i]==cnt&&dis[i]-dis[fa[i][0]]>=ans) return true;
    return false;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n-1;i++){
        int x,y,w;
        scanf("%d%d%d",&x,&y,&w);
        adde(x,y,w);adde(y,x,w);
        summ+=w;
    }
    dis[1]=0;
    dfs(1,0,1);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&lu[i].u,&lu[i].v);
        lu[i].lcaa=lca(lu[i].u,lu[i].v);
        lu[i].diss=dis[lu[i].u]+dis[lu[i].v]-2*dis[lu[i].lcaa];
    }
    int left=0,right=summ;
    int mid;
    while(left<right){
        mid=(left+right)>>1;
        if(check(mid)) right=mid;
        else left=mid+1;
    }
    printf("%d",left);
    return 0;
}

原文地址:https://www.cnblogs.com/wzxbeliever/p/11625907.html

时间: 2024-10-11 02:06:59

图论训练之四的相关文章

[图论训练]BZOJ 3245: 最快路线【最短路】

Description 精 明的小R每每开车出行总是喜欢走最快路线,而不是最短路线.很明显,每条道路的限速是小R需要考虑的关键问题.不过有一些限速标志丢失了,于是小R将不知 道能开多快.不过有一个合理的方法是进入这段道路时不改变速度行驶.你的任务就是计算从小R家(0号路口)到D号路口的最快路线. 现在你得到了这个城市的地图,这个地图上的路都是单向的,而且对于两个路口A和B,最多只有一条道路从A到B.并且假设可以瞬间完成路口的转弯和加速. Input 第一行是三个整数N,M,D(路口数目,道路数目

[图论训练]1143: [CTSC2008]祭祀river 二分图匹配

Description 在遥远的东方,有一个神秘的民族,自称Y族.他们世代居住在 水面上,奉龙王为神.每逢重大庆典, Y族都会在水面上举办盛大的祭祀活动.我们可以把Y族居住地水系看成一个由岔口和河道组成的网络.每条河道连接着两个岔口,并且水在河道内按照一个固定的 方向流动.显然,水系中不会有环流(下图描述一个环流的例子). 由于人数众多的原因,Y族的祭祀活动会在多个岔口上同时举行.出于对龙王的尊重,这些祭祀地点的选择必须非常慎重.准确地说,Y族人认为,如果水流 可以从一个祭祀点流到另外一个祭祀点

[图论训练]BZOJ 1624: [Usaco2008 Open] Clear And Present Danger 寻宝之路【floyd】

Description 农夫约翰正驾驶一条小艇在牛勒比海上航行. 海上有N(1≤N≤100)个岛屿,用1到N编号.约翰从1号小岛出发,最后到达N号小岛.一 张藏宝图上说,如果他的路程上经过的小岛依次出现了 Ai,A2,…,AM(2≤M≤10000)这样的序列(不一定相邻),那他最终就能找到古老的宝藏.  但是,由于牛勒比海有海盗出没.约翰知道任意两 个岛屿之间的航线上海盗出没的概率,他用一个危险指数Dij(0≤Dij≤100000)来描述.他希望他的寻宝活动经过的航线危险指数之和最小.那么, 在

图论训练

Light OJ 1002 - Country Roads 1.Dijkstra 1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int N=555; 5 int d[N]; 6 bool vis[N]; 7 const int INF=0x3f3f3f3f; 8 vector < pair<int,int> > E[N]; 9 10 void init(){ 11 for(int i=0;i<

图论训练之一

noip水题系列 https://www.luogu.org/problem/P1027 题目明显就是一个多组数据(也就<=10)单源最短路, 这里可以就不用floyed 但你会发现它是道蓝题也是有一定道理的 你会很恼火它的建边: 使用勾股定理加一系列的特判,而且又是浮点数 话不多述, 思路简单,代码麻烦,就把题解修改一下搬过来了 code: #include <cstdio> #include <cmath> #include <cstring> #includ

图论训练之二

https://www.luogu.org/problem/P1613 题目描述 小A的工作不仅繁琐,更有苛刻的规定,要求小A每天早上在6:00之前到达公司,否则这个月工资清零.可是小A偏偏又有赖床的坏毛病.于是为了保住自己的工资,小A买了一个十分牛B的空间跑路器,每秒钟可以跑2^k千米(k是任意自然数).当然,这个机器是用longint存的,所以总跑路长度不能超过maxlongint千米.小A的家到公司的路可以看做一个有向图,小A家为点1,公司为点n,每条边长度均为一千米.小A想每天能醒地尽量

字符串训练之四

https://www.luogu.org/problem/P2264 看一眼就是水题 方法应该很多,都可以乱搞出来 我就找了两个比较好的做法 一个是trie,还有一个是set trie #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<queue> #include<stack> #include<map> #inc

图论训练之三

https://www.luogu.org/problem/P2149 算是找到了一道好题吧 看样例直接提醒我们两点间的最短路可能不止一条 看套路: 如何判断一条边是否在这两点(S,T)最短路径上 solution:S跑一遍単源最短路,T跑一遍単源最短路,再枚举边,判断!!!!! 至于为什么要跑两遍,很好理解,因为他是単源最短路, 自行体会,实在不行看代码就懂了 同理这一道题,分别四个点跑一次単源最短路,判断两次就可以 但问题来了,因为最短路可能不止一条, 这样你就不可能把所以成立的边都加起来

图论训练之七

https://nanti.jisuanke.com/t/A1108 本题又叫缺点最短路,数据卡的很好, 一N×N×N×N恰好过不了 二N×N×N×logN才行 如果一的话就可以再在floyed的基础上多枚举一维 这一维表示不经过该点 floyed的本质是一个增量算法,最外一维枚举的是k,但这个顺序并不影响最后的结果 如果可以处理处对于每个点Y,只剩Y没在floyed的转移矩阵里, 这个矩阵的值就是不经过 y 点的全源最短路 考虑分治, 为什么要分治呢 因为一算法的不好在于每次排除一个点都要所有