P2387 [NOI2014]魔法森林 lct维护链路

题目描述

为了得到书法大家的真传,小 E 同学下定决心去拜访住在魔法森林中的隐士。魔法森林可以被看成一个包含 nn 个节点 mm 条边的无向图,节点标号为 1,2,3,…,n1,2,3,…,n,边标号为 1,2,3,…,m1,2,3,…,m。初始时小 E 同学在 11 号节点,隐士则住在 nn 号节点。小 E 需要通过这一片魔法森林,才能够拜访到隐士。

魔法森林中居住了一些妖怪。每当有人经过一条边的时候,这条边上的妖怪 就会对其发起攻击。幸运的是,在 11 号节点住着两种守护精灵:A 型守护精灵与 B 型守护精灵。小 E 可以借助它们的力量,达到自己的目的。

只要小 E 带上足够多的守护精灵,妖怪们就不会发起攻击了。具体来说,无向图中的每一条边 e_iei​ 包含两个权值 a_iai​ 与 b_ibi​ 。若身上携带的 A 型守护精灵个数不少于 a_iai​ ,且 B 型守护精灵个数不少于 b_ibi​ ,这条边上的妖怪就不会对通过这条边的人发起攻击。当且仅当通过这片魔法森林的过程中没有任意一条边的妖怪向 小 E 发起攻击,他才能成功找到隐士。

由于携带守护精灵是一件非常麻烦的事,小 E 想要知道,要能够成功拜访到 隐士,最少需要携带守护精灵的总个数。守护精灵的总个数为 A 型守护精灵的个数与 B 型守护精灵的个数之和。

输入格式

输入文件的第 11 行包含两个整数 n,mn,m,表示无向图共有 nn 个节点,mm 条边。 接下来 mm 行,第 i+1i+1 行包含 44 个正整数 X_i,Y_i,a_i,b_iXi​,Yi​,ai​,bi​,描述第 ii 条无向边。 其中 X_iXi​ 与 Y_iYi​ 为该边两个端点的标号,a_iai​ 与 b_ibi​ 的含义如题所述。 注意数据中可能包含重边与自环。

输出格式

输出一行一个整数:如果小 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\to 2\to 41→2→4,需要携带 19+15=3419+15=34 个守护精灵; 如果小 E 走路径 1\to 3\to 41→3→4,需要携带 17+17=3417+17=34 个守护精灵; 如果小 E 走路径 1\to 2\to 3\to 41→2→3→4,需要携带 19+17=3619+17=36 个守护精灵; 如果小 E 走路径 1\to 3\to 2\to 41→3→2→4,需要携带 17+15=3217+15=32 个守护精灵。 综上所述,小 E 最少需要携带 3232 个守护精灵。

* 解释2

小 E 无法从 11 号节点到达 33 号节点,故输出 -1

题解:

每条边有两个属性ai和bi 我们把边按照ai排序 lct维护bi的最大值  然后依次往lct维护的森林中加入 如果当一条边加入的时候形成了环(假如加入边e(u,v)形成了环) 那么我们要找链路(u,v)上bi值最大的边  把它与当前边的bi值比较  如果它的值大于当前边的bi 我们把这个bi值最大的边去掉  把当前边连上  通过lct判断 1和n的连通性 如果连通就取链(1,n)上bi的最大值与当前边的ai的和更新答案(当前边的ai可能不在链(1,n)上 但是它不会覆盖答案的最小值) 

#include<bits/stdc++.h>
#define lc c[x][0]
#define rc c[x][1]
#define I inline void
#define R register int
using namespace std;
const int N = 3e5+10;
int f[N],c[N][2],r[N],val[N],mx[N],st[N];
inline int in(){
	int w=0,x=0;char c=0;
	while(c>'9'||c<'0') w|=c=='-',c=getchar();
	while(c<='9'&&c>='0') x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return w?-x:x;
}
inline bool nroot(R x){
	return c[f[x]][0]==x||c[f[x]][1]==x;
}
I pushup(R x){
	mx[x]=x;
	if(val[mx[x]]<val[mx[lc]]) mx[x]=mx[lc];
	if(val[mx[x]]<val[mx[rc]]) mx[x]=mx[rc];
}
I pushr(R x){
	swap(lc,rc);
	r[x]^=1;
}
I pushdown(R x){
	if(r[x]){
		if(lc) pushr(lc);
		if(rc) pushr(rc);
		r[x]=0;
	}
}
I rotate(R x){
	R y=f[x],z=f[y],k=c[y][1]==x,w=c[x][k^1];
	if(nroot(y)) c[z][c[z][1]==y]=x; c[x][k^1]=y;c[y][k]=w;
	if(w) f[w]=y; f[x]=z;f[y]=x;
	pushup(y);
}
I splay(R x){
	R y=x,z=0;
	st[++z]=y;
	while(nroot(y)) st[++z]=y=f[y];
	while(z) pushdown(st[z--]);
	while(nroot(x)){
		y=f[x],z=f[y];
		if(nroot(y)) rotate((c[y][1]==x)^(c[z][1]==y)?x:y);
		rotate(x);
	}
	pushup(x);	
}
I access(R x){
	for(R y=0;x;x=f[y=x])
	splay(x),rc=y,pushup(x); 
}
I makeroot(R x){
	access(x);splay(x);
	pushr(x);
}
I split(R x,R y){
	makeroot(x);
	access(y);splay(y);
}
inline int findroot(R x){
	access(x);splay(x);
	while(lc) pushdown(x),x=lc;
	splay(x);
	return x;
}
inline bool judge(R x,R y){
	makeroot(x);
	return findroot(y)==x;
}
I link(R x,R y){
	makeroot(x);
	if(findroot(y)!=x) f[x]=y; 
}
I cut(R x,R y){
	makeroot(x);
	if(findroot(y)==x&&f[y]==x&&c[y][0]==0){
		f[y]=c[x][1]=0;
		pushup(x);
	}
}
struct edge{
	int x,y,a,b;
	bool operator < (const edge& t)const{
		return a<t.a;
	} 
}e[N];
int main(){
	int n,m;
	n=in();m=in();
	for(int i = 1; i <= m; i++){
		e[i].x=in();e[i].y=in();
		e[i].a=in();e[i].b=in();
	}
	sort(e+1,e+1+m);
	int ans = 2e9;
	for(int i = 1; i <= m; i++){
		val[i+n]=e[i].b;
		mx[i+n]=i+n;
		int u=e[i].x,v=e[i].y;
		if(judge(u,v)){
			split(u,v);
			int now=mx[v];
			if(val[now]>e[i].b){
				cut(now,e[now-n].x);cut(now,e[now-n].y);
				link(i+n,u);link(i+n,v);
			}
		}else{
			link(u,i+n);link(v,i+n);
		}
		if(judge(1,n)){
			split(1,n);
			ans=min(ans,val[mx[n]]+e[i].a);
		}
	}
	if(ans==2e9) printf("-1\n");
	else printf("%d\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43824564/article/details/106520485