• 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”(不 含引号)。

    输入输出样例

    输入样例#1:
    4 5 
    1 2 19 1 
    2 3 8 12 
    2 4 12 15 
    1 3 17 8 
    3 4 1 17 
    输出样例#1:
    32
    
    输入样例#2:
    3 1 
    1 2 1 1 
    输出样例#2:
    -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
    ");
    	else printf("%d
    ",ans);
    }
    
    int main()
    {
    	init();
    	solve();
    	return 0;
    }
    

  • 相关阅读:
    array_udiff_assoc — 带索引检查计算数组的差集,用回调函数比较数据
    array_sum — 对数组中所有值求和
    array_splice — 去掉数组中的某一部分并用其它值取代
    array_slice — 从数组中取出一段
    array_multisort — 对多个数组或多维数组进行排序
    array_merge — 合并一个或多个数组
    array_keys — 返回数组中部分的或所有的键名
    array_key_exists — 检查数组里是否有指定的键名或索引
    array_intersect_assoc — 带索引检查计算数组的交集
    array_flip — 交换数组中的键和值
  • 原文地址:https://www.cnblogs.com/Mychael/p/8282870.html
Copyright © 2020-2023  润新知