【NOI2014】 魔法森林---解题报告
传送门
题目大意
给定 \(n\) 个点和 \(m\) 条边。每条边包含起点终点和两个精灵的最低限制,求最少需要携带的精灵数量。
题目解析
直接套 LCT 板子
将所有边按照进行升序排序,从小到大将边加入,在已经加入边的图上找路径的最大值,求出最大值和当前枚举的和用于更新全局的最小值答案。
为什么呢?因为要 \(a\),\(b\) 都满足才能通过某条边,所以结果必定为某条边的 \(a_i\) 或 \(b_i\), 因此可以固定 \(a\) 的信息来降低复杂度。即每次选取小于等于 \(a_i\) 大小的边去维护一条 \(1\) 到 \(n\) 的路径.
动态加边,维护最大值。
直接套 LCT 板子!!
虽然题目给出的是一张图, 但实际上只需要维护出一条从 \(1\) 到 \(n\) 的路径即可.因此当新加入一条边会使维护的树变成图时, 就需要去找到环, 若新边比环中最大值小, 那么将环中的最大边删去, 加入新边即可。
然后要注意用并查集(好像是卡常,因为 yxc 直接用的并查集)
利用动态树的特性快速求路径上的最大点权值。最后注意一下删边时的编号映射。(关于我忘了切断子树卡了半天15pts艹)
代码实现
#include <bits/stdc++.h>
#define int long long
#define rint register int
#define endl '\n'
using namespace std;
const int N = 1e6 + 5;
int n, m, p[N], stk[N], ans = 0x3f3f3f3f;
struct Edge
{
int x, y, a, b;
bool operator<(const Edge &t) const { return a < t.a; }
} e[N];
struct node
{
int s[2], p, v, mx, rev;
} tr[N];
int inline min(int a, int b)
{
return a < b ? a : b;
}
int inline find(int x)
{
if (p[x] != x)
p[x] = find(p[x]);
return p[x];
}
void inline pushrev(int u)
{
swap(tr[u].s[0], tr[u].s[1]);
tr[u].rev ^= 1;
return;
}
void inline pushup(int u)
{
tr[u].mx = u;
int ll = tr[tr[u].s[0]].mx;
int rr = tr[tr[u].s[1]].mx;
if (tr[ll].v > tr[tr[u].mx].v)
{
tr[u].mx = ll;
}
if (tr[rr].v > tr[tr[u].mx].v)
{
tr[u].mx = rr;
}
return;
}
void inline pushdown(int u)
{
if (tr[u].rev)
{
pushrev(tr[u].s[0]);
pushrev(tr[u].s[1]);
tr[u].rev = 0;
}
return;
}
bool inline isroot(int u)
{
return tr[tr[u].p].s[0] != u && tr[tr[u].p].s[1] != u;
}
void inline rotate(int x)
{
int y = tr[x].p;
int z = tr[y].p;
int k = tr[y].s[1] == x;
if (!isroot(y))
{
tr[z].s[tr[z].s[1] == y] = x;
}
tr[x].p = z;
tr[y].s[k] = tr[x].s[k ^ 1], tr[tr[x].s[k ^ 1]].p = y;
tr[x].s[k ^ 1] = y, tr[y].p = x;
pushup(y);
pushup(x);
return;
}
void inline splay(int x)
{
int top = 0, r = x;
stk[++top] = r;
while (!isroot(r))
{
stk[++top] = r = tr[r].p;
}
while (top)
{
pushdown(stk[top--]);
}
while (!isroot(x))
{
int y = tr[x].p, z = tr[y].p;
if (!isroot(y))
{
if ((tr[z].s[1] == y) ^ (tr[y].s[1] == x))
rotate(x);
else
rotate(y);
}
rotate(x);
}
return;
}
void inline access(int x)
{
int z = x;
for (rint y = 0; x; y = x, x = tr[y].p)
{
splay(x);
tr[x].s[1] = y, pushup(x);
}
splay(z);
return;
}
void inline makeroot(int x)
{
access(x);
pushrev(x);
return;
}
int inline findroot(int x)
{
access(x);
while (tr[x].s[0])
{
pushdown(x);
x = tr[x].s[0];
}
splay(x);
return x;
}
void inline split(int x, int y)
{
makeroot(x);
access(y);
return;
}
void inline link(int x, int y)
{
makeroot(x);
if (findroot(y) != x)
tr[x].p = y;
return;
}
void inline cut(int x, int y)
{
makeroot(x);
if (findroot(y) == x && tr[x].s[1] == y && !tr[y].s[0])
{
tr[y].p = tr[x].s[1] = 0;
pushup(x);
}
return;
}
signed main()
{
cin >> n >> m;
for (rint i = 1; i <= m; i++)
{
int x, y, a, b;
cin >> x >> y >> a >> b;
e[i] = {x, y, a, b};
}
sort(e + 1, e + 1 + m);
for (rint i = 1; i <= n + m; i++)
{
p[i] = i;
if (i > n)
tr[i].v = e[i - n].b;
tr[i].mx = i;
}
for (rint i = 1; i <= m; i++)
{
int x = e[i].x;
int y = e[i].y;
int a = e[i].a;
int b = e[i].b;
if (find(x) == find(y))
{
split(x, y);
int t = tr[y].mx;
if (tr[t].v > b)
{
cut(t, e[t - n].x);
cut(t, e[t - n].y);
link(i + n, x);
link(i + n, y);
}
}
else
{
p[find(x)] = find(y);
link(i + n, x);
link(i + n, y);
}
if (find(1) == find(n))
{
split(1, n);
ans = min(ans, a + tr[tr[n].mx].v);
}
}
if (ans != 0x3f3f3f3f)
{
cout << ans << endl;
return 0;
}
puts("-1");
return 0;
}
【NOI2014】 魔法森林---解题报告的更多相关文章
- 洛谷 P2387 [NOI2014]魔法森林 解题报告
P2387 [NOI2014]魔法森林 题目描述 为了得到书法大家的真传,小 E 同学下定决心去拜访住在魔法森林中的隐 士.魔法森林可以被看成一个包含 n 个节点 m 条边的无向图,节点标号为 1,2 ...
- NOI2014魔法森林题解报告
题目描述 为了得到书法大家的真传,小 E 同学下定决心去拜访住在魔法森林中的隐 士.魔法森林可以被看成一个包含 n 个节点 m 条边的无向图,节点标号为 1,2,3,-,n,边标号为 1,2,3,-, ...
- NOI2014 魔法森林
3669: [Noi2014]魔法森林 Time Limit: 30 Sec Memory Limit: 512 MBSubmit: 106 Solved: 62[Submit][Status] ...
- bzoj 3669: [Noi2014]魔法森林 动态树
3669: [Noi2014]魔法森林 Time Limit: 30 Sec Memory Limit: 512 MBSubmit: 363 Solved: 202[Submit][Status] ...
- BZOJ 3669: [Noi2014]魔法森林( LCT )
排序搞掉一维, 然后就用LCT维护加边MST. O(NlogN) ------------------------------------------------------------------- ...
- bzoj 3669: [Noi2014]魔法森林
bzoj 3669: [Noi2014]魔法森林 Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号 ...
- BZOJ_3669_[Noi2014]魔法森林_LCT
BZOJ_3669_[Noi2014]魔法森林_LCT Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节 ...
- bzoj 3669: [Noi2014]魔法森林 (LCT)
链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3669 题面: 3669: [Noi2014]魔法森林 Time Limit: 30 Sec ...
- 「luogu2387」[NOI2014] 魔法森林
「luogu2387」[NOI2014] 魔法森林 题目大意 \(n\) 个点 \(m\) 条边的无向图,每条边上有两个权值 \(a,b\),求从 \(1\) 节点到 \(n\) 节点 \(max\{ ...
- P2387 [NOI2014]魔法森林(LCT)
P2387 [NOI2014]魔法森林 LCT边权维护经典题 咋维护呢?边化为点,边权变点权. 本题中我们把边对关键字A进行排序,动态维护关键字B的最小生成树 加边后出现环咋办? splay维护最大边 ...
随机推荐
- IIC、SPI、UART三者对比
下面将对比三者的各自差异: 参考资料: 1.(112条消息) UART, SPI, IIC的详解及三者的区别和联系_iic spi uart_静思心远的博客-CSDN博客
- 王道oj/problem20
网址:oj.lgwenda.com/problem/20 思路:层次建树,用递归的方法前序遍历 代码: #define _CRT_SECURE_NO_WARNINGS#include <stdi ...
- 【python】调用钉钉机器人发起通知
有时候需要做个某些服务的状态监控,用钉钉机器人发通知挺方便的.可以用shell脚本配合crontab检测状态,检测到异常就调用python脚本发起告警. python内容 此处用的python3,需要 ...
- 10、Spring之AOP概述
10.1.概念 AOP(Aspect Oriented Programming)是一种设计思想,是软件设计领域中的面向切面编程 AOP是面向对象编程(OOP)的一种补充和完善,OOP是纵向继承机制,A ...
- Unity TextMeshPro 添加中文字体遇见的问题以及解决方案
前言 按标准官方教程为 Unity TextMeshPro 添加中文字体时出现了各种奇奇怪怪的问题,于是有了这篇随笔. 中文字体解决方案 以下步骤适用于 TextMeshPro 3.0.6. 字符数量 ...
- 【matplotlib基础】--子图
使用Matplotlib对分析结果可视化时,比较各类分析结果是常见的场景.在这类场景之下,将多个分析结果绘制在一张图上,可以帮助用户方便地组合和分析多个数据集,提高数据可视化的效率和准确性. 本篇介绍 ...
- 【译】.NET 8 拦截器(interceptor)
通常情况下,出于多种原因,我不会说我喜欢写关于预览功能的文章.我的大多数帖子旨在帮助人们解决他们可能遇到的问题,而不是找个肥皂盒或打广告.但是我认为我应该介绍这个 .NET 预览特性,因为它是我在 . ...
- Prompt 指北:如何写好 Prompt,让 GPT 的回答更加精准
目录 1. 得亏 GPT 脾气好 2. 玩 GPT 得注意姿势 3. 指南指北指东指西 3.1 首先你得理解 GPT 是咋工作的 3.2 "Prompt 工程"走起 3.3 奇淫技 ...
- 如何get一个终身免费续期的定制数字人?
想拥有一个"数字分身" 吗?给你一个终身免费续期的特权. 定制周期长?训练.运营成本高?成片效果生硬?无法应用于实际场景? 随着AIGC技术的快速发展,虚拟数字人的生成效率不断提高 ...
- BZ全景可视化编辑器 (KRPano可视化编辑器, 无需编写任何代码制作全景漫游)
软件简介 BZ全景编辑器是一款KRPano全景可视化编辑工具,下载安装即可使用,无需拥有任何KRPano代码基础,便可以制作生成精美的全景漫游作品. 官方网站: 点击进入官方网站 最新版软件下载地址: ...