[Luogu 1967] NOIP2013 货车运输

<题目链接>


一年多前令我十分头大的老题终于可以随手切掉了…

然而我这码风又变毒瘤了,我也很绝望。

看着一年前不带类不加空格不空行的清纯码风啊,时光也好像回去了一样。//其实一年前我在这题上试过写类来着,结果,当然是写挂了啊。

众人:别废话了,赶紧讲题!

Capella:好,好…


对于每两个点之间的路径,我们希望路上的最小限重尽可能大,以确保运输更多的货物。所以,我们总是会选择限重尽可能大的路去走。

于是对原图(的每一个连通块)求最大生成树,将问题转化为树上问题。两个点间的的路径可通过求 LCA(最近公共祖先)得到。

LCA 考虑倍增算法(树剖不想写了),使用两个 Sparse Table(通称 ST 表),一个存树上路径,一个存树上限重最小值。

讲具体些,f[i][j] 记录编号为 i 的点向上走 \(2^j\) 步到达的点,g[i][j]记录从 i 到 f[i][j] 这段路径中的最小限重。

递推预处理,然后在线询问就好。

如果你刚才用了 Kruskal 求 MST,那么 Kruskal 算法过程中用过的并查集不要扔,洗干净裹上面粉,蛋液,面包糠,下锅炸至两面金黄后捞出,隔壁家的熊孩子都馋哭了。对于每一组询问 (x, y),看一眼两个点是否属于同一并查集,不是就直接 -1。

如果你用了 Prim,记得跑的时候记一下连通块,用于判断询问的两个点是否连通。

跑就行了,求路径上最小限重。

上代码

#include <algorithm>
#include <climits>
#include <cmath>
#include <cstdio>
#include <cstring> const int MAXN=100010, MAXM=500010; int n, m, q; struct Edge
{
int u, v, w;
void Read(void)
{
scanf("%d %d %d", &u, &v, &w);
}
bool operator <(const Edge &rhs) const
{
return w>rhs.w;
}
}s[MAXM]; struct Graph
{
int *depth;
struct Edge
{
int to, w;
Edge *next;
Edge(int to, int w, Edge* next): to(to), w(w), next(next){}
~Edge(void)
{
if(next!=nullptr)
delete next;
}
}**head;
Graph(int n): depth(new int[n+1]), head(new Edge*[n+1])
{
memset(depth, 0, (n<<2)+4);
for(int i=1; i<=n; ++i)
head[i]=nullptr;
}
~Graph(void)
{
delete[] depth;
for(int i=1; i<=n; ++i)
delete head[i];
delete[] head;
}
void AddEdges(int u, int v, int w)
{
head[u]=new Edge(v, w, head[u]);
head[v]=new Edge(u, w, head[v]);
}
}*G; class UFS
{
private:
int *f;
public:
UFS(int n): f(new int[n+1])
{
for(int i=1; i<=n; ++i)
f[i]=i;
}
~UFS(void)
{
delete[] f;
}
int Find(int x)
{
return x==f[x] ? x : f[x]=Find(f[x]);
}
bool Merge(int x, int y)
{
int a=Find(x), b=Find(y);
if(a==b)
return false;
f[b]=a;
return true;
}
}*S; class SparseTable
{
private:
int N, **f, **g;
void DFS(int u, int k)
{
G->depth[u]=k;
int v;
for(auto i=G->head[u]; i!=nullptr; i=i->next)
if(!G->depth[v=i->to])
{
f[v][0]=u;
g[v][0]=i->w;
DFS(v, k+1);
}
}
public:
SparseTable(int n): N(log2(n)), f(new int*[n+1]), g(new int*[n+1])
{
for(int i=1; i<=n; ++i)
{
f[i]=new int[N];
g[i]=new int[N];
}
for(int i=1; i<=n; ++i)
if(!G->depth[i])
{
f[i][0]=i;
g[i][0]=INT_MAX;
DFS(i, 1);
}
for(int j=1; j<=N; ++j)
for(int i=1; i<=n; ++i)
{
f[i][j]=f[f[i][j-1]][j-1];
g[i][j]=std::min(g[i][j-1], g[f[i][j-1]][j-1]);
}
}
~SparseTable(void)
{
for(int i=1; i<=n; ++i)
{
delete[] f[i];
delete[] g[i];
}
delete[] f;
delete[] g;
}
int LCA(int x, int y)
{
if(S->Find(x)^S->Find(y))
return -1;
int ans=INT_MAX;
if(G->depth[x]<G->depth[y])
std::swap(x, y);
for(int i=N; i>=0; --i)
if(G->depth[f[x][i]]>=G->depth[y])
{
ans=std::min(ans, g[x][i]);
x=f[x][i];
}
if(x==y)
return ans;
for(int i=N; i>=0; --i)
if(f[x][i]^f[y][i])
{
ans=std::min(ans, std::min(g[x][i], g[y][i]));
x=f[x][i];
y=f[y][i];
}
return ans=std::min(ans, std::min(g[x][0], g[y][0]));
}
}*ST; void Kruskal(void)
{
std::sort(s+1, s+m+1);
S=new UFS(n);
G=new Graph(n);
for(int i=1; i<=m; ++i)
if(S->Merge(s[i].u, s[i].v))
G->AddEdges(s[i].u, s[i].v, s[i].w);
} int main(void)
{
scanf("%d %d", &n, &m);
for(int i=1; i<=m; ++i)
s[i].Read();
Kruskal();
ST=new SparseTable(n);
scanf("%d", &q);
for(int i=1, x, y; i<=q; ++i)
{
scanf("%d %d", &x, &y);
printf("%d\n", ST->LCA(x, y));
}
delete S;
delete G;
delete ST;
return 0;
}

谢谢阅读

[Luogu 1967] NOIP2013 货车运输的更多相关文章

  1. [luogu P1967][NOIp2013] 货车运输

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

  2. NOIP2013 货车运输(最大生成树,倍增)

    NOIP2013 货车运输(最大生成树,倍增) A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物,司机们想知道 ...

  3. NOIP2013 货车运输 (最大生成树+树上倍增LCA)

    死磕一道题,中间发现倍增还是掌握的不熟 ,而且深刻理解:SB错误毁一生,憋了近2个小时才调对,不过还好一遍AC省了更多的事,不然我一定会疯掉的... 3287 货车运输 2013年NOIP全国联赛提高 ...

  4. NOIP2013 货车运输

    3.货车运输 (truck.cpp/c/pas) [问题描述] A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货 ...

  5. Codevs3278[NOIP2013]货车运输

    3287 货车运输 2013年NOIP全国联赛提高组  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 钻石 Diamond      题目描述 Description A 国有 ...

  6. 【洛谷P1967】[NOIP2013]货车运输

    货车运输 题目链接 显然,从一点走到另一点的路径中,最小值最大的路径一定在它的最大生成树上 所以要先求出最大生成树,再在生成树上找最近公共祖先,同时求出最小值. #include<iostrea ...

  7. noip2013货车运输

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

  8. 洛谷—— P1967 货车运输 || COGS——C 1439. [NOIP2013]货车运输

    https://www.luogu.org/problem/show?pid=1967#sub  ||  http://www.cogs.pro/cogs/problem/problem.php?pi ...

  9. NOIP2013货车运输[lca&&kruskal]

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

随机推荐

  1. OpenCV学习笔记——腐蚀与膨胀

    1.膨胀 此操作将图像 与任意形状的内核 (),通常为正方形或圆形,进行卷积. 内核 有一个可定义的 锚点, 通常定义为内核中心点. 进行膨胀操作时,将内核 划过图像,将内核 覆盖区域的最大相素值提取 ...

  2. 遇到Intel MKL FATAL ERROR: Cannot load libmkl_avx2.so or libmkl_def.so问题的解决方法

    运行一个基于tensorflow的模型时,遇到Intel MKL FATAL ERROR: Cannot load libmkl_avx2.so or libmkl_def.so问题. 解决方法:打开 ...

  3. Requests库常用方法及其详解

    request库七个方法详解 1. request方法 所有方法的的基础方法,三个参数:method,url,**kwargs. 1.1 method:请求方式 method参数共有七个可选的值,分别 ...

  4. 寒假作业2:简化电梯设计elevator

    Github仓库地址:hua-kui 寒假学习计划:学习计划 - 题目背景 一栋10层的大楼(楼层编号1-10),设有一台无限载重的电梯,初始时电梯停在1层.电梯移动1层的耗时为1,在某一层停靠的耗时 ...

  5. lintcode-248-统计比给定整数小的数的个数

    248-统计比给定整数小的数的个数 给定一个整数数组 (下标由 0 到 n-1,其中 n 表示数组的规模,数值范围由 0 到 10000),以及一个 查询列表.对于每一个查询,将会给你一个整数,请你返 ...

  6. mnist测试

    第一步:进入caffe目录 第二步:获取mnist数据集 ./data/mnist/get_mnist.sh 第三步:创建lmdb ./examples/mnist/create_mnist.sh 第 ...

  7. jdbc 6.0

    1.获取数据库自动生成的键值 package com.rong.jielong; import java.sql.Connection; import java.sql.DriverManager; ...

  8. "firstday"-软件工程

    阅读以下文章 http://www.thea.cn/news/terminal/9/9389.html    http://www.shzhidao.cn/system/2015/09/22/0102 ...

  9. scrum站立会议介绍

    什么是站立会议? 站立会议是敏捷软件开发方法论Scrum的相关技术之一,亦可称之为Scrum的最佳实践.具体形式为每天的同一时间,一个敏捷开发团队的所有成员面对面站在一起,进行一个为期15~20分钟的 ...

  10. (七)对Jmeter进行参数化的俩种方式

    一.使用CSV Data Set Config: 1.添加CSV Data Set Config: 2.配置参数: Filename:文件名,指保存参数化数据的文件目录,可以相对或者绝对路径. Fil ...