[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. Scrum Meeting Beta - 4

    Scrum Meeting Beta - 4 NewTeam 2017/12/2 地点:新主楼F座二楼 任务反馈 团队成员 完成任务 计划任务 安万贺 完成了部分页面标题栏颜色的修改和字体的调整Iss ...

  2. 201621123037 《Java程序设计》第13周学习总结

    作业13-网络 标签(空格分隔): Java 1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常相关内容. 思维导图: 其他: 网络编程:由客户端和服务器组成 - 服务器端 第一 ...

  3. Netty系列学习

    Netty系列之Netty高性能之道 Netty系列之Netty线程模型 Netty系列之Netty 服务端创建 Netty系列之Netty编解码框架分析 Netty系列之Netty百万级推送服务设计 ...

  4. 第203天:js---Array对象常用方法

    1.shift:删除原数组的第一项,返回删除元素的值:如果数组为空则返回undefined //shift:删除原数组的第一项,返回删除元素的值:如果数组为空则返回undefined var arr ...

  5. VBA-从周课表统计节次

    Sub datainsert() Dim r1 As Integer, r2 As Integer, i As Integer, j As Integer, findrow As Integer, f ...

  6. UVA12545_Bits Equalizer

    题目意思很简单,给你两个串,第一个串为0,1或者?,第二个串为0,1, 每次你可以对第一个串进行三种操作,1.0变为1:2.?变为0或者1:3.交换任意两个数的位置. 现在问你能否把第一个串变为第一个 ...

  7. Python常忘的基础知识

    0.目录 1.进制 1.1 各进制的表示 1.2 各进制的转换 2.字符 2.1 转义字符 2.2 原始字符串 3.类型 3.1 基本数据类型 3.2 type() 4.变量与运算符 4.1 值类型与 ...

  8. 20135239益西拉姆 Linux内核分析 操作系统是怎样工作的?

    益西拉姆+ 原创作品+ <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ” 堆栈 堆栈是C语言程序运行时 ...

  9. bzoj4010: [HNOI2015]菜肴制作(拓扑排序+贪心+堆)

    这题不是求最小字典序...撕烤了半个小时才发现不对劲T T 这题是能让小的尽量前就尽量前,无论字典序...比如1能在2前面就一定要在2前面... 显然是要先拓扑排序,让小的尽量前转化成让大的尽量往后丢 ...

  10. python基础----数据类型二

    数据类型 计算机顾名思义就是可以做数学计算的机器,因此,计算机程序理所当然地可以处理各种数值.但是,计算机能处理的远不止数值,还可以处理文本.图形.音频.视频.网页等各种各样的数据,不同的数据,需要定 ...