【刷题】BZOJ 3669 [Noi2014]魔法森林

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

【输入样例1】

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

【输出样例1】

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

Solution

LCT

看这道题,就是维护MST嘛

但是有两个量怎么办?

离线,把边按照 (a) 的权值排序,枚举 (a) 的值,把边权小于等于当前枚举的值的边加入LCT,而LCT维护 (b) 的MST

这样就做完了(考虑了每一个 (a) 的情况下的最优解,取个 (min) 就是答案)

#include<bits/stdc++.h>
#define ll long long
#define db double
#define ld long double
const int MAXN=50000+10,MAXM=100000+10,inf=0x3f3f3f3f;
int n,m,ans=inf,limita,fa[MAXN];
struct edge{
	int u,v,a,b;
	inline bool operator < (const edge &A) const {
		return a<A.a;
	};
};
edge side[MAXM];
#define lc(x) ch[(x)][0]
#define rc(x) ch[(x)][1]
struct LCT{
	int ch[MAXN+MAXM][2],fa[MAXN+MAXM],rev[MAXN+MAXM],Mx[MAXN+MAXM],id[MAXN+MAXM],val[MAXN+MAXM],stack[MAXN+MAXM],cnt;
	inline void init()
	{
		memset(Mx,0,sizeof(Mx));
		memset(id,0,sizeof(id));
		memset(ch,0,sizeof(ch));
		memset(fa,0,sizeof(fa));
		memset(rev,0,sizeof(rev));
	}
	inline bool nroot(int x)
	{
		return lc(fa[x])==x||rc(fa[x])==x;
	}
	inline void reverse(int x)
	{
		std::swap(lc(x),rc(x));
		rev[x]^=1;
	}
	inline void pushup(int x)
	{
		Mx[x]=val[x],id[x]=x;
		if(Mx[lc(x)]>Mx[x])Mx[x]=Mx[lc(x)],id[x]=id[lc(x)];
		if(Mx[rc(x)]>Mx[x])Mx[x]=Mx[rc(x)],id[x]=id[rc(x)];
	}
	inline void pushdown(int x)
	{
		if(rev[x])
		{
			if(lc(x))reverse(lc(x));
			if(rc(x))reverse(rc(x));
			rev[x]=0;
		}
	}
	inline void rotate(int x)
	{
		int f=fa[x],p=fa[f],c=(rc(f)==x);
		if(nroot(f))ch[p][rc(p)==f]=x;
		fa[ch[f][c]=ch[x][c^1]]=f;
		fa[ch[x][c^1]=f]=x;
		fa[x]=p;
		pushup(f);
		pushup(x);
	}
	inline void splay(int x)
	{
		cnt=0;
		stack[++cnt]=x;
		for(register int i=x;nroot(i);i=fa[i])stack[++cnt]=fa[i];
		while(cnt)pushdown(stack[cnt--]);
		for(register int y=fa[x];nroot(x);rotate(x),y=fa[x])
			if(nroot(y))rotate((lc(y)==x)==(lc(fa[y])==y)?y:x);
		pushup(x);
	}
	inline void access(int x)
	{
		for(register int y=0;x;x=fa[y=x])splay(x),rc(x)=y,pushup(x);
	}
	inline int findroot(int x)
	{
		access(x);splay(x);
		while(lc(x))pushdown(x),x=lc(x);
		splay(x);
		return x;
	}
	inline void makeroot(int x)
	{
		access(x);splay(x);reverse(x);
	}
	inline void split(int x,int y)
	{
		makeroot(x);access(y);splay(y);
	}
	inline void link(int x,int y)
	{
		makeroot(x);
		if(findroot(y)!=x)fa[x]=y;
	}
	inline void cut(int x,int y)
	{
		makeroot(x);
		if(findroot(y)==x&&fa[y]==x&&!lc(y))lc(x)=fa[y]=0,pushup(x);
	}
};
LCT T;
#undef lc
#undef rc
template<typename T> inline void read(T &x)
{
	T data=0,w=1;
	char ch=0;
	while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
	if(ch=='-')w=-1,ch=getchar();
	while(ch>='0'&&ch<='9')data=((T)data<<3)+((T)data<<1)+(ch^'0'),ch=getchar();
	x=data*w;
}
template<typename T> inline void write(T x,char c='')
{
	if(x<0)putchar('-'),x=-x;
	if(x>9)write(x/10);
	putchar(x%10+'0');
	if(c!='')putchar(c);
}
template<typename T> inline void chkmin(T &x,T y){x=(y<x?y:x);}
template<typename T> inline void chkmax(T &x,T y){x=(y>x?y:x);}
template<typename T> inline T min(T x,T y){return x<y?x:y;}
template<typename T> inline T max(T x,T y){return x>y?x:y;}
inline int found(int x)
{
	if(fa[x]!=x)fa[x]=found(fa[x]);
	return fa[x];
}
int main()
{
	read(n);read(m);
	for(register int i=1;i<=n;++i)fa[i]=i;
	T.init();
	for(register int i=1;i<=m;++i)
	{
		read(side[i].u),read(side[i].v),read(side[i].a),read(side[i].b);
		chkmax(limita,side[i].a);
	}
	std::sort(side+1,side+m+1);
	for(register int a=1,sp=1;a<=limita;++a)
	{
		while(sp<=m&&side[sp].a<=a)
		{
			int x=found(side[sp].u),y=found(side[sp].v);
			if(x!=y)
			{
				fa[x]=y;
				T.val[sp+n]=side[sp].b;
				T.link(sp+n,side[sp].u),T.link(sp+n,side[sp].v);
			}
			else
			{
				T.split(side[sp].u,side[sp].v);
				int so=T.id[side[sp].v],sn=n+sp;
				if(side[sp].b<T.Mx[side[sp].v])
				{
					T.val[sn]=side[sp].b;
					T.cut(so,side[so-n].u);T.cut(so,side[so-n].v);
					T.link(sn,side[sp].u);T.link(sn,side[sp].v);
				}
			}
			++sp;
		}
		if(found(1)==found(n))T.split(1,n),chkmin(ans,a+T.Mx[n]);
	}
	if(ans==inf)write(-1,'
');
	else write(ans,'
');
	return 0;
}
原文地址:https://www.cnblogs.com/hongyj/p/8709664.html