树链剖分+线段树

思路

貌似题解里没有树链剖分和线段树的,贡献一发。

首先明确题目要求:一辆车走某条路从x城到y城的边权最小值

我们把要求分开来看:

  1. 从x城到y城:我们需要走的路径将两点联通

  2. 边权最小值:我们要找这条路上的限重最小值

如果你是一个货车司机(而且题目还告诉你你的汽车走多远不要油),你肯定想多运一些货物,也就要求联通两点的权值尽可能大。

又要保证联通,又要保证权值尽可能大,没错,我们需要用到最小生成树。

(如果还不理解,你可以设想一下,有两条都可以从a到b,一条路限重10,一条路限重100,你一定会选择第二条路;我们再推广一下,如果两条路都能联通还未联通的a、b两个联通块(你可以认为a、b是两个岛,两条路是跨岛大桥),一条路限重10,一条路限重100,你还是一定会选择第二条路)

最小生成树的方法:先按边权大小排序,利用并查集判断两块是否联通,生成一个新的图


好,现在第一个问题解决了:你运货的最大路径方案一定在新的图(树)上了,怎么求两点之间权值最小的呢?

因为这是一棵树,所以两点之间路径唯一,可是直接搜索时间又肯定承受不住,我们这时就可以采用树链剖分了

这是类似树剖板题的题,就有提到求某两点的最值问题

值得一提的是:树剖+线段树只是支持修改和查询点权的,这时我们就需要知道怎么将边权转换为点权

边权与点权之间的转换

随便在网上找了个图:我们这样实现边权与点权之间的转换:将根节点的点权设为INF,然后所有边权下放到连接的点(所有边权往下挪到了点里,由于根节点值为INF不影响min的计算(同理,查询最大值就设为-INF))

然后直接查询就好啦!

怎么可能?!

刚开始的时候,我转换完后就直接像树剖板题那样求最值了,结果只有10分,那么问题出在哪呢?

我们看一下这个图(黑色是边权,黄色是转换后的点权):

若想查询A点到B点的最值,我们会发现,按普通树剖的查询方法,我们会访问20那个点(5-20-19-8),然而应该访问的路径是5-19-8,所以我们要对查询函数做一些修改,“绕开那些点”

void getans(int x,int y){
if(findfather(x) != findfather(y)){
printf("-1\n");
return ;
}
int ans = INF;
while(top[x] != top[y]){
if(dep[top[x]] < dep[top[y]])swap(x,y);
ans = min(ans,query(1,pos[top[x]],pos[x]));
x = fa[top[x]];
}
if(x == y){
printf("%d\n",ans);//绕开
return ;
}
if(dep[x] > dep[y])swap(x,y);
ans = min(ans,query(1,pos[x] + 1,pos[y]));//+1绕开
printf("%d\n",ans);
}

AC代码

#include<iostream>
#include<vector>
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int RD(){
int out = 0,flag = 1;char c = getchar();
while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
return flag * out;
}
const int maxn = 500190,INF = 999999999;
int num,nr,nume,na,cnt,numt;
int head[maxn];
struct Node{
int v,nxt,dis;
}E[maxn * 2];
void add(int u,int v,int dis){
E[++nume].nxt = head[u];
E[nume].v = v;
E[nume].dis = dis;
head[u] = nume;
}
struct R{
int u,v,dis;
}I[maxn];
bool cmp(R a,R b){
return a.dis > b.dis;
}
int father[maxn];
int findfather(int v){
if(father[v] == v)return v;
return father[v] = findfather(father[v]);
}
void Union(int a,int b){
int faA = findfather(a);
int faB = findfather(b);
if(faA != faB)father[faA] = faB;
}
void buildG(){//建最小生成树
for(int i = 1;i <= nr;i++){
if(findfather(I[i].u) != findfather(I[i].v)){
add(I[i].u,I[i].v,I[i].dis);
add(I[i].v,I[i].u,I[i].dis);
Union(I[i].u,I[i].v);
}
}
}
int dep[maxn],fa[maxn],wson[maxn],top[maxn],size[maxn],pos[maxn],ori[maxn];
int val[maxn];
int vis[maxn];
void dfs1(int id,int F){
vis[id] = true;
numt++;
size[id] = 1;
for(int i = head[id];i;i = E[i].nxt){
int v = E[i].v;
if(v == F)continue;
dep[v] = dep[id] + 1;
fa[v] = id;
val[v] = E[i].dis;
dfs1(v,id);
size[id] += size[v];
if(size[v] > size[wson[id]]){
wson[id] = v;
}
}
}
void dfs2(int id,int TP){
top[id] = TP;
pos[id] = ++cnt;
ori[cnt] = id;
if(!wson[id])return ;
dfs2(wson[id],TP);
for(int i = head[id];i;i = E[i].nxt){
int v = E[i].v;
if(v == fa[id] || v == wson[id])continue;
dfs2(v,v);
}
}
#define lid (id << 1)
#define rid (id << 1) | 1
struct sag_tree{
int l,r;
int min;
int lazy;
}tree[maxn << 2];
void build(int id,int l,int r){
tree[id].l = l;
tree[id].r = r;
if(l == r){
tree[id].min = val[ori[r]];
return ;
}
int mid = l + r >> 1;
build(lid,l,mid);
build(rid,mid + 1,r);
tree[id].min = min(tree[lid].min,tree[rid].min);
}
int query(int id,int l,int r){
if(tree[id].l == l && tree[id].r == r){
return tree[id].min;
}
int mid = tree[id].l + tree[id].r >> 1;
if(mid < l){
return query(rid,l,r);
}
else if(mid >= r){
return query(lid,l,r);
}
else{
return min(query(lid,l,mid),query(rid,mid + 1,r));
}
}
void getans(int x,int y){
if(findfather(x) != findfather(y)){
printf("-1\n");
return ;
}
int ans = INF;
while(top[x] != top[y]){
if(dep[top[x]] < dep[top[y]])swap(x,y);
ans = min(ans,query(1,pos[top[x]],pos[x]));
x = fa[top[x]];
}
if(x == y){
printf("%d\n",ans);
return ;
}
if(dep[x] > dep[y])swap(x,y);
ans = min(ans,query(1,pos[x] + 1,pos[y]));
printf("%d\n",ans);
}
int main(){
num = RD();nr = RD();
for(int i = 1;i <= num;i++){
father[i] = i;
}
for(int i = 1;i <= nr;i++){
I[i].u = RD();
I[i].v = RD();
I[i].dis = RD();
}
sort(I + 1,I + 1 + nr,cmp);
buildG();
int s = 1;
while(s <= num){
if(vis[s] == false){
dep[s] = 1;
val[s] = INF;
dfs1(s,-1);
dfs2(s,s);
}
s++;
}
build(1,1,numt);
na = RD();
int u,v;
for(int i = 1;i <= na;i++){
u = RD();v = RD();
getans(u,v);
}
return 0;
}

最后,感谢大佬的帮助

大佬

广告

题解 P1967 【货车运输】的更多相关文章

  1. 题解 P1967 货车运输

    题目描述 A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物,司机们想知道每辆车在不超过车辆限重的情况下,最多能 ...

  2. luogu题解P1967货车运输--树链剖分

    题目链接 https://www.luogu.org/problemnew/show/P1967 分析 NOIp的一道裸题,直接在最大生成树上剖分取最小值一下就完事了,非常好写,常数也比较小,然而题解 ...

  3. 洛谷 P1967 货车运输

    洛谷 P1967 货车运输 题目描述 A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物, 司机们想知道每辆车在 ...

  4. P1967 货车运输

    P1967 货车运输最大生成树+lca+并查集 #include<iostream> #include<cstdio> #include<queue> #inclu ...

  5. 洛谷P3379lca,HDU2586,洛谷P1967货车运输,倍增lca,树上倍增

    倍增lca板子洛谷P3379 #include<cstdio> struct E { int to,next; }e[]; ],anc[][],log2n,deep[],n,m,s,ne; ...

  6. Luogu P1967 货车运输(Kruskal重构树)

    P1967 货车运输 题面 题目描述 \(A\) 国有 \(n\) 座城市,编号从 \(1\) 到 \(n\) ,城市之间有 \(m\) 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 \ ...

  7. 【杂题总汇】NOIP2013(洛谷P1967) 货车运输

    [洛谷P1967] 货车运输 重做NOIP提高组ing... +传送门-洛谷P1967+ ◇ 题目(copy from 洛谷) 题目描述 A国有n座城市,编号从1到n,城市之间有m条双向道路.每一条道 ...

  8. 洛谷 P1967 货车运输(克鲁斯卡尔重构树)

    题目描述 AAA国有nn n座城市,编号从 11 1到n nn,城市之间有 mmm 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 qqq 辆货车在运输货物, 司机们想知道每辆车在不超过车 ...

  9. P1967 货车运输(倍增LCA,生成树)

    题目链接: https://www.luogu.org/problemnew/show/P1967 题目描述 A国有n座城市,编号从 1到n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制, ...

  10. 洛谷 P1967 货车运输 Label: 倍增LCA && 最小瓶颈路

    题目描述 A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多 ...

随机推荐

  1. Daily Scrum (2015/11/3)

    今天我们的爬虫能在pc上成功运行并且把所爬取的数据存到服务器上了!我们已经搭建好数据库,把相关信息存到数据库中,并把数据存到D盘里共享给数据处理小组使用. 成员 今日工作 时间 明日工作 符美潇 完成 ...

  2. 20162328蔡文琛 week09 大二

    20162328蔡文琛 大二week09 教材学习内容总结 堆是一棵完全二叉树,其中每个元素大于等于其所有子节点的值. 向堆中添加一个元素的方法是,首先将这个元素添加为叶节点然后将其向上移动到合适的位 ...

  3. cocos2d-x 相关文章资源(安卓开发)

    http://blog.csdn.net/sdhjob/article/details/38734993 http://www.cnblogs.com/code4app/p/4026665.html ...

  4. Task 9 从用户界面和体验分析“360极速浏览器”

    我目前使用的浏览器是360极速浏览器,下面将针对用户界面.记住用户选择.短期刺激.长期使用的好处坏处.不要让用户犯简单的错误四个方面对其进行评估: 1.用户界面: 01 可视性原则--网络没有连接或者 ...

  5. java中方法传入参数时:值传递还是址传递?

    JAVA中的数据类型有两大类型: ① 基本数据类型:逻辑型(boolean).文本型(char).整数型(byte.short.int.long).浮点型(float.double) ② 引用数据类型 ...

  6. java集合ArrayList

    基于jdk_1.8.0 关于List,主要是有序的可重复的数据结构.jdk主要实现类有ArrayList(底层使用数组).LinkedList(底层使用双向链表) ArrayList: (一)继承关系 ...

  7. Java网络编程一:基础知识详解

    网络基础知识 1.OSI分层模型和TCP/IP分层模型的对应关系 这里对于7层模型不展开来讲,只选择跟这次系列主题相关的知识点介绍. 2.七层模型与协议的对应关系 网络层   ------------ ...

  8. java 数据结构与算法---栈

    原理来自百度百科 一.栈的定义 栈是一种只能在一端进行插入和删除操作的特殊线性表:它按照先进后出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数 ...

  9. 二叉树 Java 实现 前序遍历 中序遍历 后序遍历 层级遍历 获取叶节点 宽度 ,高度,队列实现二叉树遍历 求二叉树的最大距离

    数据结构中一直对二叉树不是很了解,今天趁着这个时间整理一下 许多实际问题抽象出来的数据结构往往是二叉树的形式,即使是一般的树也能简单地转换为二叉树,而且二叉树的存储结构及其算法都较为简单,因此二叉树显 ...

  10. mysql 查询缓存优化文章

    还不错 http://www.jzxue.com/shujuku/mysql/200910/20-2981.html