[NOI 2014]魔法森林
Description
为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士。魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M。初始时小E同学在号节点1,隐士则住在号节点N。小E需要通过这一片魔法森林,才能够拜访到隐士。
魔法森林中居住了一些妖怪。每当有人经过一条边的时候,这条边上的妖怪就会对其发起攻击。幸运的是,在号节点住着两种守护精灵:A型守护精灵与B型守护精灵。小E可以借助它们的力量,达到自己的目的。
只要小E带上足够多的守护精灵,妖怪们就不会发起攻击了。具体来说,无向图中的每一条边Ei包含两个权值Ai与Bi。若身上携带的A型守护精灵个数不少于Ai,且B型守护精灵个数不少于Bi,这条边上的妖怪就不会对通过这条边的人发起攻击。当且仅当通过这片魔法森林的过程中没有任意一条边的妖怪向小E发起攻击,他才能成功找到隐士。
由于携带守护精灵是一件非常麻烦的事,小E想要知道,要能够成功拜访到隐士,最少需要携带守护精灵的总个数。守护精灵的总个数为A型守护精灵的个数与B型守护精灵的个数之和。
Input
第1行包含两个整数N,M,表示无向图共有N个节点,M条边。 接下来M行,第行包含4个正整数Xi,Yi,Ai,Bi,描述第i条无向边。其中Xi与Yi为该边两个端点的标号,Ai与Bi的含义如题所述。 注意数据中可能包含重边与自环。
Output
输出一行一个整数:如果小E可以成功拜访到隐士,输出小E最少需要携带的守护精灵的总个数;如果无论如何小E都无法拜访到隐士,输出“-1”(不含引号)。
Sample Input
4 5
1 2 19 1
2 3 8 12
2 4 12 15
1 3 17 8
3 4 1 17
【输入样例2】
3 1
1 2 1 1
Sample Output
32
【样例说明1】
如果小E走路径1→2→4,需要携带19+15=34个守护精灵;
如果小E走路径1→3→4,需要携带17+17=34个守护精灵;
如果小E走路径1→2→3→4,需要携带19+17=36个守护精灵;
如果小E走路径1→3→2→4,需要携带17+15=32个守护精灵。
综上所述,小E最少需要携带32个守护精灵。
【输出样例2】
-1
【样例说明2】
小E无法从1号节点到达3号节点,故输出-1。
HINT
2<=n<=50,000
0<=m<=100,000
1<=ai ,bi<=50,000
题解
$LCT$ 维护边权信息的题...但据说动态 $SPFA$ 能水过,我这么菜,像这种 $NOI$ 的题也只能靠水才能做得了...
贪心是把边按 $a$ 排序,动态加边,边权为 $b$ 。每加一条边,从边的两端点开始跑 $SPFA$ , $SPFA$ 维护路径上的瓶颈。然后取最后加的边的 $a$ 值 + $SPFA$ 过程中维护的起点到终点的最小化瓶颈值 $b$ 的和的最小值。
注意的是动态 $SPFA$ 和 [HNOI 2014]道路堵塞 是一样的,不要去清空 $dist$ 数组。
//It is made by Awson on 2018.1.10
#include <set>
#include <map>
#include <cmath>
#include <ctime>
#include <queue>
#include <stack>
#include <cstdio>
#include <string>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define lowbit(x) ((x)&(-(x)))
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
#define Swap(a, b) ((a) ^= (b), (b) ^= (a), (a) ^= (b))
using namespace std;
const int N = ;
const int M = ;
const int INF = ~0u>>;
void read(int &x) {
char ch; bool flag = ;
for (ch = getchar(); !isdigit(ch) && ((flag |= (ch == '-')) || ); ch = getchar());
for (x = ; isdigit(ch); x = (x<<)+(x<<)+ch-, ch = getchar());
x *= -*flag;
}
void write(int x) {
if (x > ) write(x/);
putchar(x%+);
} int n, m, ans = INF;
struct ss {
int u, v, a, b;
bool operator < (const ss &tmp) const {
return a < tmp.a;
}
}e[M+];
struct tt {int to, next, cost; }edge[(M<<)+];
int path[N+], top;
int q[N+], head, tail, vis[N+], dist[N+];
void add(int u, int v, int c) {edge[++top].to = v, edge[top].next = path[u], edge[top].cost = c, path[u] = top; }
int SPFA(int u, int v) {
vis[q[head = tail = ] = u] = , ++tail, vis[q[tail] = v] = , ++tail;
while (head != tail) {
int u = q[head]; ++head, head %= N, vis[u] = ;
for (int i = path[u]; i; i = edge[i].next) {
int v = edge[i].to;
if (dist[v] > Max(dist[u], edge[i].cost)) {
dist[v] = Max(dist[u], edge[i].cost);
if (!vis[v]) vis[q[tail] = v] = , ++tail, tail %= N;
}
}
}
return dist[n];
}
void work() {
read(n), read(m);
for (int i = ; i <= m; i++) read(e[i].u), read(e[i].v), read(e[i].a), read(e[i].b);
sort(e+, e++m); for (int i = ; i <= n; i++) dist[i] = INF;
for (int i = ; i <= m; i++) {
add(e[i].u, e[i].v, e[i].b), add(e[i].v, e[i].u, e[i].b);
int tmp = SPFA(e[i].u, e[i].v); if (tmp != INF) ans = Min(ans, tmp+e[i].a);
}
if (ans != INF) write(ans);
else putchar('-'), putchar('');
}
int main() {
work();
return ;
}
动态SPFA
我已经这么菜了,肯定不能再怠惰...留个坑,用 $lct$ 再写一遍...
upd 18.1.11:填坑,码完 $lct$ 了。
对于边权问题的处理,用 $lct$ 不好简易地将边权转移为点权。解决这类问题的方法就是再开一倍点,将边变为点,表示边的点权就是原边权,而表示点的点权赋为 $0$ 。
对于这道题,方法和上面的一样,按照 $a$ 排序, $lct$ 维护最小瓶颈路。
//It is made by Awson on 2018.1.11
#include <set>
#include <map>
#include <cmath>
#include <ctime>
#include <queue>
#include <stack>
#include <cstdio>
#include <string>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
#define Swap(a, b) ((a) ^= (b), (b) ^= (a), (a) ^= (b))
using namespace std;
const int N = ;
const int INF = ~0u>>; int n, m, w[N+], a[N+], b[N+], ans = INF;
struct tt {
int u, v, a, b;
bool operator < (const tt &tmp) const {
return a < tmp.a;
}
}e[N+];
struct Link_Cut_Tree {
Link_Cut_Tree() {for (int i = ; i <= N; i++) isrt[i] = ; }
int pre[N+], ch[N+][], rev[N+], maxi[N+], isrt[N+], pos;
void pushup(int o) {
if (!o) return;
maxi[o] = o;
if (w[maxi[ch[o][]]] > w[maxi[o]]) maxi[o] = maxi[ch[o][]];
if (w[maxi[ch[o][]]] > w[maxi[o]]) maxi[o] = maxi[ch[o][]];
}
void pushdown(int o) {
if (!rev[o] || !o) return;
int ls = ch[o][], rs = ch[o][];
Swap(ch[ls][], ch[ls][]), Swap(ch[rs][], ch[rs][]);
rev[ls] ^= , rev[rs] ^= , rev[o] = ;
}
void push(int o) {if (!isrt[o]) push(pre[o]); pushdown(o); }
void rotate(int o, int kind) {
int p = pre[o];
ch[p][!kind] = ch[o][kind], pre[ch[o][kind]] = p;
if (isrt[p]) isrt[o] = , isrt[p] = ;
else ch[pre[p]][ch[pre[p]][] == p] = o;
pre[o] = pre[p];
ch[o][kind] = p, pre[p] = o;
pushup(p), pushup(o);
}
void splay(int o) {
push(o);
while (!isrt[o]) {
if (isrt[pre[o]]) rotate(o, ch[pre[o]][] == o);
else {
int p = pre[o], kind = ch[pre[p]][] == p;
if (ch[p][kind] == o) rotate(o, !kind), rotate(o, kind);
else rotate(p, kind), rotate(o, kind);
}
}
}
void access(int o) {
int y = ;
while (o) {
splay(o);
isrt[ch[o][]] = , isrt[ch[o][] = y] = ;
pushup(o); o = pre[y = o];
}
}
void makeroot(int o) {access(o), splay(o); rev[o] ^= , Swap(ch[o][], ch[o][]); }
int find(int o) {access(o), splay(o); while (ch[o][]) o = ch[o][]; return o; }
void link(int x, int y) {makeroot(x); pre[x] = y; }
void cut(int x, int y) {makeroot(x); access(y), splay(y); ch[y][] = pre[x] = , isrt[x] = ; pushup(y); }
int query(int x, int y) {
if (find(x)^find(y)) return INF;
makeroot(x), access(y), splay(y);
return maxi[y];
}
void update(int x, int y, int c) {
int last = query(x, y);
if (last == INF) {w[++pos] = c, a[pos] = x, b[pos] = y; link(x, pos), link(y, pos); return; }
if (w[last] <= c) return;
cut(last, a[last]), cut(last, b[last]);
w[last] = c, a[last] = x, b[last] = y; link(x, last), link(y, last);
}
}T;
void work() {
scanf("%d%d", &n, &m); T.pos = n;
for (int i = ; i <= m; i++) scanf("%d%d%d%d", &e[i].u, &e[i].v, &e[i].a, &e[i].b);
sort(e+, e++m);
for (int i = ; i <= m; i++) {
T.update(e[i].u, e[i].v, e[i].b);
int tmp = T.query(, n); if (tmp != INF) ans = Min(ans, e[i].a+w[tmp]);
}
if (ans == INF) ans = -; printf("%d\n", ans);
}
int main() {
work();
return ;
}
link-cut-tree
[NOI 2014]魔法森林的更多相关文章
- 【BZOJ3669】[Noi2014]魔法森林 LCT
终于不是裸的LCT了...然而一开始一眼看上去这是kruskal..不对,题目要求1->n的路径上的每个点的两个最大权值和最小,这样便可以用LCT来维护一个最小生成路(瞎编的...),先以a为关 ...
- BZOJ 3669 【NOI2014】 魔法森林
Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M.初始时小E同学在号节 ...
- BZOJ-3669 魔法森林 Link-Cut-Tree
意识到背模版的重要性了,记住了原理和操作,然后手打模版残了..颓我时间...... 3669: [Noi2014]魔法森林 Time Limit: 30 Sec Memory Limit: 512 M ...
- 【BZOJ】3669: [Noi2014]魔法森林(lct+特殊的技巧)
http://www.lydsy.com/JudgeOnline/problem.php?id=3669 首先看到题目应该可以得到我们要最小化 min{ max{a(u, v)} + max{b(u, ...
- [NOI 2014]做题记录
[NOI2014]起床困难综合症 按位贪心 #include <algorithm> #include <iostream> #include <cstring> ...
- 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]魔法森林
Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M.初始时小E同学在号节 ...
- BZOJ 3669: [Noi2014]魔法森林( LCT )
排序搞掉一维, 然后就用LCT维护加边MST. O(NlogN) ------------------------------------------------------------------- ...
随机推荐
- 关于hbase中的hbase-site.xml 配置详解
该文档是用Hbase默认配置文件生成的,文件源是 hbase-default.xml hbase.rootdir 这个目录是region server的共享目录,用来持久化HBase.URL需要是'完 ...
- Java连接mysql——Establishing SSL connection without server's identity verification is not recommended.
Establishing SSL connection without server's identity verification is not recommended. 出现这个错误的原因是因为m ...
- Software Engineering-HW3 264&249
title: Software Engineering-HW3 date: 2017-10-05 10:04:08 tags: HW --- 小组成员 264 李世钰 249 王成科 项目地址 htt ...
- 201621123062《Java程序设计》第一周学习总结
1.本周学习总结 关键词: 初步熟悉Java的基本组成.语言特点(简单性.结构中立性).运行环境.简单语法等. 关键概念之间的联系: 1.JVM是Java程序唯一认识的操作系统,其可执行文件为.cla ...
- Java暑期作业
一.假期观影笔记--<熔炉> 影片<熔炉>是根据发生在韩国光州聋哑学校里的真实事件而改编.影片讲述的是在一所聋哑儿童学校里,校长.教务以及老师披着慈善的华丽外衣对学校中的多名未 ...
- Python 图片转字符画
Python 图片转字符画 一.课程介绍 1. 课程来源 原创 2. 内容简介 本课程讲述怎样使用 Python 将图片转为字符画 3. 前置课程 Python编程语言 Linux 基础入门(新版) ...
- 201621123031 《Java程序设计》第1周学习总结
作业01-Java基本概念 1.本周学习总结 1.本周学习内容:Java发展史(简述).Java语言特点.JDK .JRE .JVM .Java的开发步骤.Java开发工具. 2.关键概念之间的联系: ...
- Autowired注解
package com.how2java.pojo; import org.springframework.beans.factory.annotation.Autowired; public cla ...
- RAID6三块硬盘离线导致的数据丢失恢复过程
小编我最近参与了一例非常成功的数据恢复的案例,在这里分享给大家.用户是一组6块750G磁盘的 RAID6,先后有两块磁盘离线,但维护人员在此情况下依然没有更换磁盘,所以在第三块硬盘离线后raid直接崩 ...
- Vue-cli+Vue.js2.0+Vuex2.0+vue-router+es6+webpack+node.js脚手架搭建和Vue开发实战
Vue.js是一个构建数据驱动的web界面的渐进式框架.在写这边文章时Vue版本分为1.0++和2.0++,这个是基于Vue2.0的项目. Vue-cli是构建单页应用的脚手架,这个可是官方的. Vu ...