[Luogu 1967] NOIP2013 货车运输
[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 货车运输的更多相关文章
- [luogu P1967][NOIp2013] 货车运输
题目描述 A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多 ...
- NOIP2013 货车运输(最大生成树,倍增)
NOIP2013 货车运输(最大生成树,倍增) A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物,司机们想知道 ...
- NOIP2013 货车运输 (最大生成树+树上倍增LCA)
死磕一道题,中间发现倍增还是掌握的不熟 ,而且深刻理解:SB错误毁一生,憋了近2个小时才调对,不过还好一遍AC省了更多的事,不然我一定会疯掉的... 3287 货车运输 2013年NOIP全国联赛提高 ...
- NOIP2013 货车运输
3.货车运输 (truck.cpp/c/pas) [问题描述] A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货 ...
- Codevs3278[NOIP2013]货车运输
3287 货车运输 2013年NOIP全国联赛提高组 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题目描述 Description A 国有 ...
- 【洛谷P1967】[NOIP2013]货车运输
货车运输 题目链接 显然,从一点走到另一点的路径中,最小值最大的路径一定在它的最大生成树上 所以要先求出最大生成树,再在生成树上找最近公共祖先,同时求出最小值. #include<iostrea ...
- noip2013货车运输
P1967 货车运输 题目描述 A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过 ...
- 洛谷—— P1967 货车运输 || COGS——C 1439. [NOIP2013]货车运输
https://www.luogu.org/problem/show?pid=1967#sub || http://www.cogs.pro/cogs/problem/problem.php?pi ...
- NOIP2013货车运输[lca&&kruskal]
题目描述 A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多 ...
随机推荐
- OC创建对象并访问成员变量
1.创建一个对象 Car *car =[Car new] 只要用new操作符定义的实体,就会在堆内存中开辟一个新空间 [Car new]在内存中 干了三件事 1)在堆中开辟一段存储空间 2)初始化成员 ...
- erlang转化中文为url
今天使用http get 方法时,参量中有中文而导致出错. 例如http://abc.com/abc?arg=中文,在erlang使用http:request方法失败. 后来查了url的规范,url中 ...
- Windows下IntelliJ IDEA中调试Spark Standalone
参考:http://dataknocker.github.io/2014/11/12/idea%E4%B8%8Adebug-spark-standalone/ 转载请注明来自:http://www.c ...
- 【Asp.Net Core】在Visual Studio 2017中使用Asp.Net Core构建Angular4应用程序
前言 Visual Studio 2017已经发布了很久了.做为集成了Asp.Net Core 1.1的地表最强IDE工具,越来越受.NET系的开发人员追捧. 随着Google Angular4的发布 ...
- HDU2486_A simple stone game
这个题目是这样的,一堆石子有n个,首先第一个人开始可以去1-(n-1)个,接下来两人轮流取石子,每个人可取的石子数必须是一个不超过上一次被取的石子的K倍的整数. 现在求对于一堆数量为n的石子是否为必胜 ...
- dom对象转成jquery对象时候 变成数组 jquery转成dom时候 取数组第一个
- 最小费用流spfa最小费用路算法模板(pascal)
3.30: 这篇是以前写的,用的还是指针存图,今天又写了个代码,码风稍微好看点. 传送门:http://www.cnblogs.com/Currier/p/6648685.html --------- ...
- 深入理解JVM一java堆分析
上一节介绍了针对JVM的监控工具,包括JPS可以查看当前所有的java进程,jstack查看线程栈可以帮助你分析是否有死锁等情况,jmap可以导出java堆文件在MAT工具上进行分析等等.这些工具都非 ...
- CF825F String Compression 解题报告
CF825F String Compression 题意 给定一个串s,其中重复出现的子串可以压缩成 "数字+重复的子串" 的形式,数字算长度. 只重复一次的串也要压. 求压缩后的 ...
- HSTS的来龙去脉
前言 安全经常说“云.管.端”,“管”指的是管道,传输过程中的安全.为了确保信息在网络传输层的安全,现在很多网站都开启了HTTPS,也就是HTTP+TLS,在传输过程中对信息进行加密.HTTPS使用了 ...