首页 > 其他分享 >「NOIP2013」货车运输 题解

「NOIP2013」货车运输 题解

时间:2023-08-21 21:48:12浏览次数:43  
标签:NOIP2013 min int 题解 货车运输 wi fa maxn ans

「NOIP2013」货车运输

前言

这道题算是一个稍有思维难度的 MST+LCA 题目了。

稍微卡了一会(0-88-88-88-100(打表)-100(打表)-100(正解)),开始是打了表过了,后面在 DCZ 的帮助下正解通过(下面注释提到的一个坑)。

题目大意

给出一张无向图 \(G\),有 \(n\) 个点和 \(m\) 个边 \((x,y)=z\) ,找到一条路径使 \((u \to v)\) 中最小的 \(w\) 最大并输出 \(w\),如果找不到这样的路径,输出 -1

题目解析

考虑最大生成树,因为最大生成树可以保证图的每个点之间依然联通且可以拿到尽可能大的边权,可以保证接下来能操作的路径之间权值更大。

这里使用 Kruskal 算法,在执行时以每条当前极大边建无向图就行了。

得到这个树形结构的图之后,为了获取两点之间的通路和最小的 \(w\) ,就需要求解 LCA(最近公共祖先)了,下面采用倍增的方式。

如果直接采用普通的 ST LCA,会发现一个问题:在求解时最小的 \(w\) 怎么获取呢?

我们可以考虑在执行预处理 pre() 时和 f[u][k] 同时维护一个数组 wi[u][k] 表示点 \(u\) 向上走 \(2^k\) 步时最小的 \(w\)。

像 \(f(u,\ i+1)=f(f(u,\ i),\ i)\) 这个式子一样,易得 \(wi(u,\ i+1)=\min(wi(u,\ i),wi(f(u,\ i),\ i))\)

为了在 pre() 中能够处理 wi[][],我们需要在 pre(u,fa) 新增一个参数 w,表示 \((fa,u)\) 的权值,以此来设置边界条件,即:\(wi(u,\ 0)=w\)

最后在 lca() 中维护一个 \(ans\) 表示 \((u \to v)\) 中最小的 \(w\),并在每次上移时更新 \(ans\),返回该值即可。

还有一个问题:如何判定两点不通?其实在运行 Kruskal 时留下的并查集就可以发挥作用,如果两点不通,肯定不是在一个集合里面的,所以只需要在 lca() 中的第一行这样子写:

if(find(x)!=find(y)) return -1;

代码

下面的代码注释会提出几个小坑,如果您自己码题时不注意您一定无法通过此题(打表除外)!

注释版

#include <bits/stdc++.h> //万能头偷懒(
using namespace std;
const int maxn=5e5+5; //注意数据范围
struct r {int u,v,w;}; //结构体,存储Kruskal时需要的边
int n,m,cnt,dep[maxn],fa[maxn],f[maxn][24],wi[maxn][24],vis[maxn];
vector<pair<int,int>> g[maxn]; //邻接表vector建图
vector<r> edge; //Kruskal用
inline void add(int u,int v,int w) {g[u].push_back(make_pair(v,w));}
inline int find(int x) {return x==fa[x]?x:fa[x]=find(fa[x]);}
void pre(int u,int fa,int w){ //预处理
    dep[u]=dep[fa]+1; //深度处理
    f[u][0]=fa,wi[u][0]=w,vis[u]=1; //vis和递推の边界条件
    for(int i=0;i<20;i++) //倍增递推
        f[u][i+1]=f[f[u][i]][i],
        wi[u][i+1]=min(wi[u][i],wi[f[u][i]][i]);
    for(auto [v,w]:g[u]) //遍历当前点扩展节点
        //auto [v,w]:g[u] 为 C++17语法,可以把结构体的两个值解包到 v,w 中(包括pair)
        if(!vis[v]) pre(v,u,w);
}
int lca(int x,int y){
    int ans=1e9; //维护的最小的w ans
    if(find(x)!=find(y)) return -1; // 不是通路
    if(dep[x]<dep[y]) swap(x,y); //交换,方便
    for(int i=20;i>=0;i--){
        if(dep[f[x][i]]>=dep[y]) //同步x和y的层级
            ans=min(ans,wi[x][i]),x=f[x][i]; //更新ans
        if(x==y) return ans; //共线,找到LCA,返回
    }
    for(int i=20;i>=0;i--) //向上试探
        if(f[x][i]!=f[y][i]) //直到LCA的下一层
            ans=min(ans,min(wi[x][i],wi[y][i])), //更新ans
            x=f[x][i],y=f[y][i]; //向上更新x,y
    return min(ans,min(wi[x][0],wi[y][0])); //再次更新最后一级,返回ans
}
inline void kruskal(){ //kruskal部分
    sort(edge.begin(),edge.end(),[](r a,r b){
        return a.w>b.w; //注意是最大生成树
    });
    for(auto [u,v,w]:edge){ //结构体解包
        int x=find(u),y=find(v); //正常部分
        if(x==y) continue;
        fa[x]=y,cnt++;
        add(u,v,w),add(v,u,w); //建无向树形图
        if(cnt==n-1) break; //已经生成一棵树了
    }
}
int main(){
    ios::sync_with_stdio(0); //关同步流
    cin>>n>>m;
    for(int i=1,a,b,w;i<=m;i++)
        cin>>a>>b>>w,edge.push_back({a,b,w}); //读入边并存入边集
    iota(fa+1,fa+1+n,1),kruskal();
    //iota在stl中稍冷门,可以在迭代器范围内从第三个参数起自增1赋值,其实很适合拿来初始化并查集
    pre(1,0,0);int x,y,q;cin>>q; //预处理,初始化部分变量
    while(q--){
        cin>>x>>y;
        if(!vis[x]) pre(x,0,0); 
        //坑:多次预处理,以防第一次从1开始的图部分点不联通导致两个不联通于1的点未成功初始化出错,不加这句只有88分qwq
		cout<<lca(x,y)<<endl;
    }
    return 0;
}
//Endl QwQ

极速版

可以CTJ的版本

#include <bits/stdc++.h>
using namespace std;
const int maxn=5e5+5;
struct r {int u,v,w;};
int n,m,cnt,dep[maxn],fa[maxn],f[maxn][24],wi[maxn][24],vis[maxn];
vector<pair<int,int>> g[maxn];
vector<r> edge;
inline void add(int u,int v,int w) {g[u].push_back(make_pair(v,w));}
inline int find(int x) {return x==fa[x]?x:fa[x]=find(fa[x]);}
void pre(int u,int fa,int w){
    dep[u]=dep[fa]+1;
    f[u][0]=fa,wi[u][0]=w,vis[u]=1;
    for(int i=0;i<20;i++)
        f[u][i+1]=f[f[u][i]][i],
        wi[u][i+1]=min(wi[u][i],wi[f[u][i]][i]);
    for(auto [v,w]:g[u])
        if(!vis[v]) pre(v,u,w);
}
int lca(int x,int y){
    int ans=1e9;
    if(find(x)!=find(y)) return -1;
    if(dep[x]<dep[y]) swap(x,y);
    for(int i=20;i>=0;i--){
        if(dep[f[x][i]]>=dep[y]) 
            ans=min(ans,wi[x][i]),x=f[x][i];
        if(x==y) return ans;
    }
    for(int i=20;i>=0;i--)
        if(f[x][i]!=f[y][i])
            ans=min(ans,min(wi[x][i],wi[y][i])),
            x=f[x][i],y=f[y][i];
    return min(ans,min(wi[x][0],wi[y][0]));
}
inline void kruskal(){
    sort(edge.begin(),edge.end(),[](r a,r b){
        return a.w>b.w;
    });
    for(auto [u,v,w]:edge){
        int x=find(u),y=find(v);
        if(x==y) continue;
        fa[x]=y,cnt++;
        add(u,v,w),add(v,u,w);
        if(cnt==n-1) break;
    }
}
int main(){
    ios::sync_with_stdio(0);
    cin>>n>>m;
    for(int i=1,a,b,w;i<=m;i++)
        cin>>a>>b>>w,edge.push_back({a,b,w});
    iota(fa+1,fa+1+n,1),kruskal();
    pre(1,0,0);int x,y,q;cin>>q;
    while(q--){
        cin>>x>>y;
        if(!vis[x]) pre(x,0,0);
		cout<<lca(x,y)<<endl;
    }
    return 0;
}

标签:NOIP2013,min,int,题解,货车运输,wi,fa,maxn,ans
From: https://www.cnblogs.com/LYXOfficial/p/17647145.html

相关文章

  • 猴王 题解 || 冷门的 pb_ds 库
    猴王前言虽然很久以前(6月)在我们学并查集的时候QYC就给我们讲了左偏树可以拿来做这道题,但是左偏树作为拓展内容还是稍有难度,最近在gcc中看到pb_ds库,发现非常好用,于是就有了这种偷懒解法。pb_ds库pb_ds库是内置于GCC中的一种拓展标准库,可以在CCF系列比赛中使用。pb......
  • 「NOIP2017 普及组」棋盘 题解
    前言一个绿题,风光啊QwQ题面传送门思路怎么走我们定义一个函数dfs(x,y,coin,can,color)x,y表示坐标,coin表示当前的金币数量,color表示当前坐标的颜色,can表示当前是否能施展魔法。再加一个mp数组记录颜色,-1表示无颜色,0表示红色,1表示黄色为什么不直接使用mp[x][y]获取颜......
  • [CF1790F] Timofey and Black-White Tree 题解
    [CF1790F]TimofeyandBlack-WhiteTree题解题目描述ZYH有一棵\(n\)个节点的树,最初\(c_0\)号节点是黑色,其余均为白色。给定操作序列\(c_1,c_2,\cdots,c_{n-1}\),第\(i\)次操作表示将\(c_i\)号节点染黑。每次操作后,输出距离最近的两个黑点间的距离。两点\(u,v\)间......
  • CF1762E Tree Sum 题解
    题意对于一棵\(n\)个节点的树\(T\),定义\(\operatorname{good}(T)\)为真当且仅当边权\(w\in\left\{-1,1\right\}\)且对于任意节点\(u\),均有\(\displaystylef(u)=\prod\limits_{\left(u,v\right)\inE}w\left(u,v\right)=-1\)。求\[\sum\limits_{\operat......
  • RTSP/Onvif视频服务器EasyNVR安防视频云服务调用接口录像会被自动删除的问题解决方案
    EasyNVR安防视频云服务是基于RTSP/Onvif协议接入的视频平台,可支持将接入的视频流进行全平台、全终端的分发,分发的视频流包括RTSP、RTMP、HTTP-FLV、WS-FLV、HLS、WebRTC等。平台丰富灵活的视频能力,可应用在智慧校园、智慧工厂、智慧水利等场景中。有用户反馈,在使用EasyNVR接入设备......
  • [ABC315G] Ai + Bj + Ck = X (1 <= i, j, k <= N) 题解
    [ABC315G]Ai+Bj+Ck=X(1<=i,j,k<=N)题解题目描述求题目中式子的数量。思路因为\(N\le10^6\),所以考虑枚举\(k\),那么变为求\(ai+bj=x-ck,i,j\in[1,N]\),这个问题可以通过Exgcd算法求解。首先考虑求出一组\(i,j\)的特解\(x',y'\),根据通解\(x=x'+......
  • Codeforces Round 893 (Div. 2) A-C题解
    CF893(Div.2)A.Buttons签到题。两人会优先选择c中的按钮来,避免自己的按钮消耗同时减少对方可选择的按钮。所以c%2==1等价于a的按钮数+1,c%2==0时相当于c按钮不存在,比较ab按钮的数量来得出答案即可。#include<iostream>usingnamespacestd;typedeflonglong......
  • [AGC005C] Tree Restoring 题解
    比较简单的题。思路我们可以把一棵树抽象成一条极长的链上挂了很多的点。观察这样的树的性质。除去中间的每一个\(dis\)至少有两个点的\(a_i=dis\)。考虑这条链的长度为\(s\)。那么对于中间的点,我们可以分两种情况讨论。\(s\)为偶数那么我们必然要求在中间的权值只......
  • hive sql运行时候reduce 只有2个问题解决
    我们在explansql时候发现width是负数,事实上原因width是通过dataSize/rowNum计算出来的,这两个参数都是在执行计划中根据每个operator通过stats计算出来的。对于selectquery来说,datasize是根据columnstats、尤其是non-null的数据计算出来的,这些non-nullvalue按照如下公......
  • RTSP/Onvif流媒体服务器EasyNVR安防视频平台一直提示网络请求失败的问题解决方案
    EasyNVR平台优秀的视频能力在于通过RTSP/ONVIF协议,将前端接入设备的音视频资源进行采集,并转码成适合全平台、全终端分发的视频流格式,包括RTMP、RTSP、FLV、HLS、WebRTC等格式。有用户反馈,EasyNVR使用过程中,突然提示网络请求失败,视频也无法播放,请求我们协助排查。此前我......