• [BZOJ3669][Noi2014]魔法森林


    [BZOJ3669][Noi2014]魔法森林

    试题描述

    为了得到书法大家的真传,小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型守护精灵的个数之和。

    输入

    第1行包含两个整数N,M,表示无向图共有N个节点,M条边。 接下来M行,第行包含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

    输出示例

    3 1
    1 2 1 1

    数据规模及约定

    2<=n<=50,000

    0<=m<=100,000

    1<=ai ,bi<=50,000

    题解

    首先按权值 a 排序,然后动态加入这些边,如果形成环了则把环上权值 b 最大的边删去。这样我们就可以保证在所有权值 a 不超过当前的 ai 时 bi 最小了,所以我们每次加完边后都求一次 1 到 n 的路径上的答案即可。

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cctype>
    #include <algorithm>
    using namespace std;
    
    int read() {
    	int x = 0, f = 1; char c = getchar();
    	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    	return x * f;
    }
    
    #define maxn 50010
    #define maxm 100010
    #define maxnode 150010
    #define oo 2147483647
    
    struct Node {
    	int a, b, mxa, mxb;
    	bool rev;
    	Node(): rev(0) {}
    	Node(int _, int __): a(_), b(__) {}
    } ns[maxnode];
    int fa[maxnode], ch[maxnode][2], S[maxnode], top;
    bool isrt(int u) { return !fa[u] || (ch[fa[u]][0] != u && ch[fa[u]][1] != u); }
    void maintain(int o) {
    	ns[o].mxa = ns[o].mxb = o;
    	for(int i = 0; i < 2; i++) if(ch[o][i]) {
    		int son = ns[ch[o][i]].mxa, &mea = ns[o].mxa;
    		if(ns[son].a > ns[mea].a) mea = son;
    		son = ns[ch[o][i]].mxb; int &meb = ns[o].mxb;
    		if(ns[son].b > ns[meb].b) meb = son;
    	}
    	return ;
    }
    void pushdown(int o) {
    	if(!ns[o].rev) return ;
    	swap(ch[o][0], ch[o][1]);
    	for(int i = 0; i < 2; i++) if(ch[o][i])
    		ns[ch[o][i]].rev ^= 1;
    	ns[o].rev = 0;
    	return ;
    }
    void rotate(int u) {
    	int y = fa[u], z = fa[y], l = 0, r = 1;
    	if(!isrt(y)) ch[z][ch[z][1]==y] = u;
    	if(ch[y][1] == u) swap(l, r);
    	fa[u] = z; fa[y] = u; fa[ch[u][r]] = y;
    	ch[y][l] = ch[u][r]; ch[u][r] = y;
    	maintain(y); maintain(u);
    	return ;
    }
    void splay(int u) {
    	int t = u; S[top = 1] = t;
    	while(!isrt(t)) S[++top] = fa[t], t = fa[t];
    	while(top) pushdown(S[top--]);
    	while(!isrt(u)) {
    		int y = fa[u], z = fa[y];
    		if(!isrt(y)) {
    			if(ch[y][0] == u ^ ch[z][0] == y) rotate(u);
    			else rotate(y);
    		}
    		rotate(u);
    	}
    	return ;
    }
    void access(int u) {
    	splay(u); ch[u][1] = 0; maintain(u);
    	while(fa[u]) splay(fa[u]), ch[fa[u]][1] = u, maintain(fa[u]), splay(u);
    	return ;
    }
    void makeroot(int u) {
    	access(u); ns[u].rev ^= 1;
    	return ;
    }
    void link(int a, int b) {
    	makeroot(b); fa[b] = a;
    	return ;
    }
    void cut(int a, int b) {
    	makeroot(a); access(b); ch[b][0] = fa[a] = 0; maintain(a);
    	return ;
    }
    int _a, _b;
    void query(int a, int b) {
    	makeroot(a); access(b);
    	_a = ns[b].mxa; _b = ns[b].mxb;
    	return ;
    }
    
    struct Edge {
    	int u, v, a, b;
    	Edge() {}
    	Edge(int _1, int _2, int _3, int _4): u(_1), v(_2), a(_3), b(_4) {}
    	bool operator < (const Edge& t) const { return a < t.a; }
    } es[maxm];
    
    int pa[maxn];
    int findset(int x) { return x == pa[x] ? x : pa[x] = findset(pa[x]); }
    
    int ans;
    
    int main() {
    	int n = read(), m = read();
    	for(int i = 1; i <= n; i++) ns[i] = Node(0, 0), maintain(i), pa[i] = i;
    	for(int i = 1; i <= m; i++) {
    		int u = read(), v = read(), a = read(), b = read();
    		es[i] = Edge(u, v, a, b);
    	}
    	sort(es + 1, es + m + 1);
    	
    	ans = oo;
    //	for(int i = 1; i <= m; i++) printf("Edge %d: %d %d %d %d
    ", i, es[i].u, es[i].v, es[i].a, es[i].b);
    	for(int i = 1; i <= m; i++) {
    		int u = es[i].u, v = es[i].v, U = findset(u), V = findset(v);
    		ns[i+n] = Node(es[i].a, es[i].b); maintain(i + n);
    		if(U == V) {
    			query(u, v);
    			if(ns[_b].b > es[i].b) {
    				cut(es[_b-n].u, _b); cut(_b, es[_b-n].v);
    				link(u, i + n); link(i + n, v);
    			}
    		}
    		else {
    			pa[V] = U;
    			link(u, i + n); link(i + n, v);
    		}
    		if(findset(1) == findset(n)) {
    			query(1, n);
    			ans = min(ans, ns[_a].a + ns[_b].b);
    		}
    	}
    	
    	printf("%d
    ", ans < oo ? ans : -1);
    	
    	return 0;
    }
    
  • 相关阅读:
    js判断是否为数字
    MongoDB 复制集机制及原理
    MongoDB 聚合查询
    C#中时间相关知识点小结
    C#中的小数和百分数计算
    WPF使用VisualTreeHelper进行复杂命中测试
    WPF使用VisualTreeHelperj简单进行命中测试
    WPF Data Binding数据驱动
    WPF Binding中的RelativeSource属性
    WPF特效:流光动画效果
  • 原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/6687798.html
Copyright © 2020-2023  润新知