NOI2014魔法森林题解报告
题目描述
为了得到书法大家的真传,小 E 同学下定决心去拜访住在魔法森林中的隐 士。魔法森林可以被看成一个包含 n 个节点 m 条边的无向图,节点标号为 1,2,3,…,n,边标号为 1,2,3,…,m。初始时小 E 同学在 1 号节点,隐士则住在 n 号节点。小 E 需要通过这一片魔法森林,才能够拜访到隐士。
魔法森林中居住了一些妖怪。每当有人经过一条边的时候,这条边上的妖怪 就会对其发起攻击。幸运的是,在 1 号节点住着两种守护精灵:A 型守护精灵与 B 型守护精灵。小 E 可以借助它们的力量,达到自己的目的。
只要小 E 带上足够多的守护精灵,妖怪们就不会发起攻击了。具体来说,无 向图中的每一条边 ei 包含两个权值 ai 与 bi 。若身上携带的 A 型守护精灵个数不 少于 ai ,且 B 型守护精灵个数不少于 bi ,这条边上的妖怪就不会对通过这条边 的人发起攻击。当且仅当通过这片魔法森林的过程中没有任意一条边的妖怪向 小 E 发起攻击,他才能成功找到隐士。
由于携带守护精灵是一件非常麻烦的事,小 E 想要知道,要能够成功拜访到 隐士,最少需要携带守护精灵的总个数。守护精灵的总个数为 A 型守护精灵的 个数与 B 型守护精灵的个数之和。
输入输出格式
输入格式:
输入文件的第 1 行包含两个整数 n,m,表示无向图共有 n 个节点,m 条边。 接下来 m 行,第i+ 1 行包含 4 个正整数 Xi,Yi,ai,bi,描述第i条无向边。 其中Xi与 Yi为该边两个端点的标号,ai 与 bi 的含义如题所述。 注意数据中可能包含重边与自环。
输出格式:
输出一行一个整数:如果小 E 可以成功拜访到隐士,输出小 E 最少需要携 带的守护精灵的总个数;如果无论如何小 E 都无法拜访到隐士,输出“-1”(不 含引号)。
输入输出样例
4 5
1 2 19 1
2 3 8 12
2 4 12 15
1 3 17 8
3 4 1 17
32
3 1
1 2 1 1
-1
说明
- 解释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
小 E 无法从 1 号节点到达 3 号节点,故输出-1。

题解
刚学完link cut tree,找题刷刷
这道题要满足两种权值和最小,我们难以同时兼顾,我们就先令a最小并维护b最小。
这就成了维护动态最小生成树
将边按a的大小升序排序,不断加边,这样子当前a的路径最大权值如果改变,那么一定是刚加入的边的权值
证明:
1、若路径经过新加入的边,由于新加入的边比之前加入的边权值都大,所以最大权值是新加入的边
2、若路径不经过新加入的边,那么这条路径之前一定被计算过,不妨令之前算出a0+b0,如今算出a1+b0,由升序得a1>a0,所以新算的路径不会被考虑,不影响结果
如此以来,我们按a升序加入,同时维护b的最小生成树【也就是当边为N-1条之后,没加入一条边就删去一条b权值最大的边,动态维护b的最小生成树】
每次统计的答案就是 当前加入的a+【1到N】b的最大值
很明显用link cut tree
把边看作点,每次加入一条边就连上两端的点,删去类似。
我们对每个节点维护子树的最大值编号,每次就可以通过link cut tree找到最大权值对应的边删去,也可以迅速统计路径b最大权值
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define isr(u) (e[e[u].f].ch[1] == u)
#define isrt(u) (!e[u].f || (e[e[u].f].ch[0] != u &&e[e[u].f].ch[1] != u))
using namespace std;
const int maxn=200005,maxm=100005,INF=100000000; inline int read(){
int out=0,flag=1;char c=getchar();
while(c<48||c>57) {if(c=='-') flag=-1;c=getchar();}
while(c>=48&&c<=57) {out=out*10+c-48;c=getchar();}
return out*flag;
} int N,M,temp[maxn]; struct EDGE{
int x,y,a,b;
}edge[maxm]; inline bool operator < (const EDGE& a,const EDGE& b){
return a.a < b.a;
} struct node{
int w,f,ch[2],rev,Maxu;
node() {f = ch[0] = ch[1] = rev = w = 0;}
}e[maxn]; inline void push_up(int u){
e[u].Maxu = u;
if (e[u].ch[0] && e[e[e[u].ch[0]].Maxu].w > e[e[u].Maxu].w) e[u].Maxu = e[e[u].ch[0]].Maxu;
if (e[u].ch[1] && e[e[e[u].ch[1]].Maxu].w > e[e[u].Maxu].w) e[u].Maxu = e[e[u].ch[1]].Maxu;
} inline void pd(int u){
if (e[u].rev){
swap(e[u].ch[0],e[u].ch[1]);
e[e[u].ch[0]].rev ^= 1;
e[e[u].ch[1]].rev ^= 1;
e[u].rev = 0;
}
} inline void push_down(int u){
int i = 0;
do {temp[++i] = u;} while(!isrt(u) && (u = e[u].f));
while (i) pd(temp[i--]);
} inline int Find(int u){
while (e[u].f) u = e[u].f;
return u;
} inline void spin(int u){
int s = isr(u),fa = e[u].f;
e[u].f = e[fa].f;
if (!isrt(fa)) e[e[fa].f].ch[isr(fa)] = u;
e[fa].ch[s] = e[u].ch[s^1];
if (e[u].ch[s^1]) e[e[u].ch[s^1]].f = fa;
e[fa].f = u;
e[u].ch[s^1] = fa;
push_up(fa);
} inline void splay(int u){
push_down(u);
while(!isrt(u)){
if (isrt(e[u].f)) spin(u);
else if (isr(u) ^ isr(e[u].f)) spin(u),spin(u);
else spin(e[u].f),spin(u);
}
push_up(u);
} inline void Access(int u){
for (int v = 0; u; u = e[v = u].f){
splay(u);
e[u].ch[1] = v;
if (v) e[v].f = u;
push_up(u);
}
} inline void Make_root(int u){
Access(u); splay(u);
e[u].rev ^= 1;
} inline void Link(int u,int v){
Make_root(u); e[u].f = v;
} inline void Cut(int u,int v){
Make_root(u); Access(v); splay(v);
e[u].f = 0;
e[v].ch[0] = 0;
push_up(v);
} inline int Query(int u,int v){
if (Find(u) != Find(v)) return 0;
Make_root(u); Access(v); splay(v);
return e[v].Maxu;
} void init(){
e[0].w = INF;
e[0].Maxu = 0;
N = read();
M = read();
for (int i = 1; i <= M + N; i++) e[i].Maxu = i;
for (int i = 1; i <= M; i++){
edge[i].x = read();
edge[i].y = read();
edge[i].a = read();
edge[i].b = read();
}
sort(edge + 1,edge + 1 + M);
} void solve(){
int ans = INF,x,y,t;
for (int i = 1; i <= M; i++){
e[N + i].w = edge[i].b;
if (Find(x = edge[i].x) != Find(y = edge[i].y)){
Link(N + i,x);
Link(N + i,y);
}else {
t = Query(x,y);
if(e[t].w > e[N + i].w){
Cut(t,edge[t - N].x);
Cut(t,edge[t - N].y);
Link(N + i,x);
Link(N + i,y);
}
}
ans = min(ans,edge[i].a + e[Query(1,N)].w);
}
if (ans == INF) printf("-1\n");
else printf("%d\n",ans);
} int main()
{
init();
solve();
return 0;
}
NOI2014魔法森林题解报告的更多相关文章
- 洛谷 P2387 [NOI2014]魔法森林 解题报告
P2387 [NOI2014]魔法森林 题目描述 为了得到书法大家的真传,小 E 同学下定决心去拜访住在魔法森林中的隐 士.魔法森林可以被看成一个包含 n 个节点 m 条边的无向图,节点标号为 1,2 ...
- BZOJ3669:[NOI2014]魔法森林——题解
http://www.lydsy.com/JudgeOnline/problem.php?id=3669 https://www.luogu.org/problemnew/show/P2387 为了得 ...
- [NOI2014]魔法森林题解
这道题正解其实是LCT,然而貌似SPFA也可以成功水过,所以根本不知道LCT的我只能说SPFA了. 这道题最大的限制是两种精灵就意味着一条道可能有两个权值,因此我们需要去将其中一个固定,然后再推另一个 ...
- [NOI2014]魔法森林 LCT
题面 [NOI2014]魔法森林 题解 一条路径的代价为路径上的\(max(a[i]) + max(b[i])\),因为一条边同时有$a[i], b[i]$2种权值,直接处理不好同时兼顾到,所以我们考 ...
- NOI2014 魔法森林
3669: [Noi2014]魔法森林 Time Limit: 30 Sec Memory Limit: 512 MBSubmit: 106 Solved: 62[Submit][Status] ...
- bzoj 3669: [Noi2014]魔法森林
bzoj 3669: [Noi2014]魔法森林 Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号 ...
- 「luogu2387」[NOI2014] 魔法森林
「luogu2387」[NOI2014] 魔法森林 题目大意 \(n\) 个点 \(m\) 条边的无向图,每条边上有两个权值 \(a,b\),求从 \(1\) 节点到 \(n\) 节点 \(max\{ ...
- 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) ------------------------------------------------------------------- ...
随机推荐
- PHP基础知识试题
转载于:http://www.php.cn/toutiao-415599.html 1.PHP中传值与传引用的区别,什么时候传值,什么时候传引用? 按值传递:函数范围内对值任何改变在函数外部都会被忽略 ...
- 如何解决Django与Vue语法的冲突
当我们在django web框架中,使用vue的时候,会遇到语法冲突.因为vue使用{{}},而django也使用{{}},因此会冲突. 解决办法1:在django1.5以后,加入了标签:{% ver ...
- python登录验证码生成及自动化测试规避
在用django写论坛的时候,需要有登录及注册功能. 故就登录界面后端需要生成随机验证码并传值给前端的代码进行编写如下. 验证码生成png需要调用到python的图形库 生成注册码img import ...
- 使用Photon引擎进行unity网络游戏开发(三)——网络游戏大厅及房间
使用Photon引擎进行unity网络游戏开发(三)--网络游戏大厅及房间 Photon PUN Unity 网络游戏开发 连接到Photon ConnectUsingSettings 设置你的客户端 ...
- 教你thinkphp5怎么配置二级域名
有些项目要将移动端和PC端分离开来,比如访问xxx.com,展示的是PC端的页面.而访问m.xxx.com,展示的是移动端的页面.thinkphp源码需要多多学习,这里记录一下知识点,顺便分享给需要的 ...
- File Transfer(并查集)
题目大意:将多个电脑通过网线连接起来,不断查询2台电脑之间是否连通. 问题来源:中国大学mooc 05-树8 File Transfer (25 分) We have a network of com ...
- Centos7.2部署saltstack
原文发表于cu:2016-06-23 参考文档: Saltstack安装文档:https://repo.saltstack.com/#rhel saltstack的安装与简单配置,应用. 一.环境 S ...
- 福利,一张图看懂IT售前工程师修炼之道
职场中的新人如何自我定位? 如何深刻理解IT售前这个职位? 如何从IT售前菜鸟成长为IT售前专家? 推荐这本书<IT售前工程师修炼之道> 本书精华内容 售前的重要性 售前要有逻辑能力 售前 ...
- 如何在Ubuntu 18.04上安装Go
如何在Ubuntu 18.04上安装Go 谢鸢发表于云计算教程系列订阅98 介绍 课程准备 第1步 - 安装Go 第2步 - 设置Go路径 第3步 - 测试您的安装 结论 介绍 Go是Google开发 ...
- 亚马逊拟斥资15亿美元建航空货运中心 - Amazon to spend $1.49 bln on air cargo hub, fans talk of bigger ambitions - ReutersFebruary 1, 2017
2月1日消息,亚马逊本周二宣布将在肯塔基州开建其第一个航空货运中心,以应对高速增长的航空货运需求.亚马逊预计,该项目将带来2000个工作岗位. 据悉,该项计划总投入约为15亿美元,亚马逊或可从当地政府 ...