Luogu P1967 货车运输(Kruskal重构树)
题面
题目描述
\(A\) 国有 \(n\) 座城市,编号从 \(1\) 到 \(n\) ,城市之间有 \(m\) 条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有 \(q\) 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。
输入输出格式
输入格式:
第一行有两个用一个空格隔开的整数 \(n,m\) ,表示 \(A\) 国有 \(n\) 座城市和 \(m\) 条道路。
接下来 \(m\) 行每行 \(3\) 个整数 \(x, y, z\) ,每两个整数之间用一个空格隔开,表示从 \(x\) 号城市到 \(y\) 号城市有一条限重为 \(z\) 的道路。注意: \(x\) 不等于 \(y\) ,两座城市之间可能有多条道路 。
接下来一行有一个整数 \(q\) ,表示有 \(q\) 辆货车需要运货。
接下来 \(q\) 行,每行两个整数 \(x,y\) ,之间用一个空格隔开,表示一辆货车需要从 \(x\) 城市运输货物到 \(y\) 城市,注意: \(x\) 不等于 \(y\) 。
输出格式:
共有 \(q\) 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出 \(-1\) 。
输入输出样例
输入样例:
4 3
1 2 4
2 3 3
3 1 1
3
1 3
1 4
1 3
输出样例:
3
-1
3
说明
对于 $30 % $ 的数据, \(0<n<1,000,0<m<10,000,0<q<1,000\) ;
对于 $60 % $ 的数据, \(0<n<1,000,0<m<50,000,0<q<1,000\) ;
对于 $100 % $ 的数据, \(0<n<10,000,0<m<50,000,0<q<30,000,0 \leq z \leq 100,000\) 。
思路
七月份的时候刚做这道题,用的是最大生成树+树链剖分。今天为了写[NOI2018]归程,把这道题当作了 \(Kruskal\) 重构树的板子题,在这里讲解一下 \(Kruskal\) 重构树的写法。
在 \(Kruskal\) 算法的过程中,如果要在 \(x,y\) 两节点之间连边,那么就把并查集中 \(x,y\) 的父节点分别连向一个新节点,这个新节点有一个权值,该点权为这条边的长度。也就是说这样子做:
sort(edge,edge+m);
for(int i=1;i<=n;i++) f[i]=i;
for(int i=0;i<m;i++)
{
int fx=fd(edge[i].u),fy=fd(edge[i].v);
if(fx!=fy)
{
a[++n]=edge[i].d;
f[fx]=f[fy]=f[n]=n;
add_edge(fx,n),add_edge(fy,n);
}
}
这样子建出来的树有以下特点:
- 该树共有 \(2n-1\) 个结点
- 原来有 \(n\) 个点,一共连了 \(n-1\) 条边,每条边新建一个结点,所以 \(n+n-1=2n-1\)
- 原有点全在叶子节点上
- 按照上述写法,
f[fx]=f[fy]=f[n]=n;,是从 \(fx,fy\) 向 \(n\) 连边,没有结点向结点连边
- 按照上述写法,
- 该树是一颗二叉树
- 显然
- 该树是一个小顶堆
- 先加入的是边权大的边,最后加入的是较小的边,较大的边所表示的结点的父亲节点是较小的边
那么这就导出了另一条神奇的性质: \(LCA(u,v)\) 为 \(u,v\) 结点路径上的最小边权结点。所以我们就可以依靠这条性质,使用倍增, \(Tarjan\) 或树链剖分等各种各样的算法求 \(LCA\) 来统计答案了。这就是 \(Kruskal\) 重构树。
顺便给一道板子题:BZOJ 3732: Network。
AC代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e4+5;
const int MAXM=5e4+5;
int n,m,f[MAXN],a[MAXN],fa[MAXN],son[MAXN],sz[MAXN],dep[MAXN],st[MAXN];
int cnt,top[MAXN],to[MAXN<<1],nex[MAXN<<1];
struct Edge
{
int u,v,d;
bool operator < (const Edge& sjf) const {return d>sjf.d;}
}edge[MAXM];
inline int read()
{
int re=0;
char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) re=(re<<3)+(re<<1)+ch-'0',ch=getchar();
return re;
}
inline void add_edge(int x,int y)
{
to[++cnt]=y,nex[cnt]=top[x],top[x]=cnt;
to[++cnt]=x,nex[cnt]=top[y],top[y]=cnt;
}
inline int fd(int x)
{
int r=x;
while(f[r]!=r) r=f[r];
int i=x,j;
while(i!=r) j=f[i],f[i]=r,i=j;
return r;
}
void dfs1(int now)
{
for(int i=top[now];i;i=nex[i])
{
if(to[i]==fa[now]) continue;
fa[to[i]]=now,dep[to[i]]=dep[now]+1,sz[to[i]]=1;
dfs1(to[i]);
sz[now]+=sz[to[i]];
if(sz[to[i]]>sz[son[now]]) son[now]=to[i];
}
}
void dfs2(int now,int line_top)
{
st[now]=line_top;
if(!son[now]) return ;
dfs2(son[now],line_top);
for(int i=top[now];i;i=nex[i])
{
if(to[i]==son[now]||to[i]==fa[now]) continue;
dfs2(to[i],to[i]);
}
}
int main()
{
n=read(),m=read();
for(int i=0;i<m;i++) edge[i].u=read(),edge[i].v=read(),edge[i].d=read();
sort(edge,edge+m);
for(int i=1;i<=n;i++) f[i]=i;
for(int i=0;i<m;i++)
{
int fx=fd(edge[i].u),fy=fd(edge[i].v);
if(fx!=fy)
{
a[++n]=edge[i].d;
f[fx]=f[fy]=f[n]=n;
add_edge(fx,n),add_edge(fy,n);
}
}
for(int i=1;i<=n;i++)
if(f[i]==i)
{
fa[i]=n,sz[i]=dep[i]=1;
dfs1(i);
dfs2(i,i);
}
m=read();
while(m--)
{
int x=read(),y=read();
if(fd(x)!=fd(y)) puts("-1");
else
{
while(st[x]!=st[y])
{
if(dep[st[x]]<dep[st[y]]) swap(x,y);
x=fa[st[x]];
}
if(dep[x]>dep[y]) swap(x,y);
printf("%d\n",a[x]);
}
}
return 0;
}
Luogu P1967 货车运输(Kruskal重构树)的更多相关文章
- Luogu P1967 货车运输
qwq 这题是知道了正解做法才写的.. 求每两点间最小权值最大的路径,本来我以为要每个点都跑一遍dij(?),后来意识到生成树好像是用来找这个的( ´▽`) 然后我问dtxdalao对不对,他说“我记 ...
- Luogu P1967 货车运输 倍增+最大生成树
看见某大佬在做,决定补一发题解$qwq$ 首先跑出最大生成树(注意有可能不连通),然后我们要求的就是树上两点间路径上的最小边权. 我们用倍增的思路跑出来$w[u][j]$,表示$u$与的它$2^j$的 ...
- LUOGU P1967 货车运输(最大生成树+树剖+线段树)
传送门 解题思路 货车所走的路径一定是最大生成树上的路径,所以先跑一个最大生成树,之后就是求一条路径上的最小值,用树剖+线段树,注意图可能不连通.将边权下放到点权上,但x,y路径上的lca的答案不能算 ...
- NOIP 2013 提高组 洛谷P1967 货车运输 (Kruskal重构树)
题目: A 国有 nn 座城市,编号从 11 到 nn,城市之间有 mm 条双向道路.每一条道路对车辆都有重量限制,简称限重. 现在有 qq 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情 ...
- 洛谷p1967货车运输(kruskal重构树)
题面 题解中有很多说最优解是kruskal重构树 所以 抽了个早自习看了看这方面的内容 我看的博客 感觉真的挺好使的 首先对于kruskal算法来说 是基于贪心的思想把边权排序用并查集维护是否是在同一 ...
- kruskal - 倍增 - 并查集 - Luogu 1967 货车运输
P1967 货车运输 题目描述 A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过 ...
- [luogu P4197] Peaks 解题报告(在线:kruskal重构树+主席树 离线:主席树+线段树合并)
题目链接: https://www.luogu.org/problemnew/show/P4197 题目: 在Bytemountains有N座山峰,每座山峰有他的高度$h_i$.有些山峰之间有双向道路 ...
- 【Luogu P5168】xtq玩魔塔(Kruskal 重构树 & 树状数组 & set)
Description 给定一个 \(n\) 个顶点,\(m\) 条边的无向联通图,点.边带权. 先有 \(q\) 次修改或询问,每个指令形如 \(\text{opt}\ x\ y\): \(\tex ...
- Luogu P4768 [NOI2018]归程(Dijkstra+Kruskal重构树)
P4768 [NOI2018]归程 题面 题目描述 本题的故事发生在魔力之都,在这里我们将为你介绍一些必要的设定. 魔力之都可以抽象成一个 \(n\) 个节点. \(m\) 条边的无向连通图(节点的编 ...
随机推荐
- UNIT对话系统(杂记)
单轮对话指标: 召回率=机器人能回答的问题数/问题总数 准确率=机器人正确回答的问题数/问题总数 问题解决率=机器成功解决的问题数/问题总数 多轮对话指标: 任务完成率=成功结束的多轮会话数/多轮会话 ...
- [NOIP2019模拟赛][AT2381] Nuske vs Phantom Thnook
题目链接 评测姬好快啊(港记号?)暴力40pts变成60pts 因为题目说了保证蓝色点两两之间只有一条路径,所以肯定组成了一棵树,而对于每次询问的x1,y1,x2,y2的子矩阵中就存在着一个森林 不难 ...
- 【模板篇】splay(填坑)+模板题(普通平衡树)
划着划着水一不小心NOIP还考的凑合了… 所以退役的打算要稍微搁置一下了… 要准备准备省选了…. 但是自己已经啥也不会了… 所以只能重新拾起来… 从splay开始吧… splay我以前扔了个板子来着, ...
- vue_cli 安装
1.安装node 2.cmd node-v3.如果是刚刚安装输入 node-v 会成功出现版本 如果不是全局安装 过段时间输入 node-v会出现 'node' 不是内部或外部命令,也不是可运行的程序 ...
- Android 开发 MediaRecorder音频录制
前言 MediaRecorder类是Android sdk提供的一个专门用于音视频录制,一般利用手机麦克风采集音频和摄像头采集图像.这个类是属于简单的音频录制类,录制音频简单容易但是对音频流的控制也比 ...
- es6 + 笔记整理
1. ES6提供了默认参数值机制,允许你为参数设置默认值,防止在函数被调用时没有传入这些参数: const required = () => {throw new Error('Missing ...
- thinkphp 高级模型
高级模型提供了更多的查询功能和模型增强功能,利用了模型类的扩展机制实现.如果需要使用高级模型的下面这些功能,记得需要继承Think\Model\AdvModel类或者采用动态模型. namespace ...
- Laravel移除Cache-Control
碰到一个问题,网站上线后,需要移除Cache-Control,就是下面这个东西 方案1 失败 参考网址:https://stackoverflow.com/questions/51821563/lar ...
- Java-Shiro:目录
ylbtech-Java-Shiro:目录 1.返回顶部 2.返回顶部 3.返回顶部 4.返回顶部 5.返回顶部 6.返回顶部 作者:ylbtech出处:http://yl ...
- LOJ10157——皇宫看守(树形DP)
传送门:QAQQAQ 题意:在一个树上放置守卫,使每一个节点都至少有相邻一节点放置守卫,使最终经费最少 思路:树形DP 首先会想到没有上司的舞会,0表示不放守卫,1表示放守卫,但考虑到对于当前点不放守 ...