[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. MySQL 备份和恢复 理论知识

    为什么要备份 数据无价   制定备份策略的注意点 1:可容忍丢失多少数据     2:恢复需要在多长时间内完成     3:备份的对象   数据.二进制日志和InnoDB的事务日志.SQL代码(存储过 ...

  2. IT就业·软件工程之我见

    随着计算机技术的飞速发展,让人们深切感受到科技让生活更美好的真正含义. 现如今我们的正常生活,社交都越来越离不开网络和终端,因特网和各种终端设备的组合让我们即使相距千里,也能面对面对话交流:购物,我们 ...

  3. Eureka Server Replicate

    为了方便说明,就把上篇博客的图再贴一遍了. 上篇说道Application Service向Eureka Server注册服务的过程,在完成注册之后,由于Eureka Server是对等集群,其他Se ...

  4. vue.cli实现tab切换效果

    <template> <div class="cp-select">                     <div class="lef ...

  5. 最小生成树-Borůvka算法

    一般求最小生成树的时候,最流行的是Kruskal算法,一种基于拟阵证明的贪心,通过给边排序再扫描一次边集,利用并查集优化得到,复杂度为\(O(ElogE)\).另一种用得比较少的是Prim算法,利用优 ...

  6. [一]SpringBoot 之 HelloWorld

    (1)新建一个Maven Java工程 (2)在pom.xml文件中添加Spring BootMaven依赖 2.1在pom.xml中引入spring-boot-start-parent spring ...

  7. Argus UVALive - 3135(优先队列 水题一道)

    有一系列的事件,它每Period秒钟就会产生编号为qNum的事件,你的任务是模拟出前k个事件,如果多个事件同时发生,先处理qNum小的事件 今天再看看数据结构.. #include <iostr ...

  8. 一些noip模拟题一句话题解

    Problem A: 序列 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 12  Solved: 9[Submit][Status][Web Boar ...

  9. NOIP2017 考前汇总

    时隔一年,相比去年一无所知的自己,学到了不少东西,虽然还是很弱,但也颇有收获[学会了打板QAQ] 现在是2017.11.9   21:10,NOIP2017的前两天晚上,明天就要出发,做最后的总结 N ...

  10. php配置说明

    1上传文件限制配置 post_max_size = 200M upload_max_file_size = 200M