Luogu2387 [NOI2014]魔法森林

Link

Solution

感觉自己做完全靠蒙和猜,尽管是正解但并不知道为什么。看了题解才知道原来是生成树满足这个性质。

暴力可以枚举\(max\{a\}\),然后在此条件下找\(max\{b\}\)最小的\(1\)\(n\)的路径。

优化可以参照kruskal的思想。

先把所有边按\(a_i\)从小到大排序。维护一个\(b\)的最小生成树。依次枚举每条边,那么此时\(a_i\)就是全局最大的\(a\)。如果加入这条边不形成环,就直接加入。如果形成环,根据生成树的回路性质,某个回路中的权值最大边恰好有一条不在最小生成树中(引用自luogu题解)。所以找到环上\(max\{b\}\),和\(b_i\)做比较,如果\(b_i\)较小就用这条边替换。

答案就是\(1\)\(n\)联通以后每次的\(a_i+max\{b_k\}(k\in Path_{1\rightarrow n})\)\(min\)\(Path_{1\rightarrow n}\)\(1\)\(n\)的路径。

考虑为什么这样是对的。如果这条边在\(1\)\(n\)路径上,显然是对的。如果不在,那么\(1\)\(n\)路径上的\(max\{a\}\)肯定不大于\(a_i\),那么一定在以前枚举过这条边,更新过答案。所以这一次并不会对答案造成影响。

Code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define REP(i,a,b) for(int i=(a),ed=(b);i<=ed;++i)
inline int read(){
	register int x=0,f=1;register char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=0;ch=getchar();}
	while(isdigit(ch)){x=x*10+(ch^'0');ch=getchar();}
	return f?x:-x;
}

const int N=5e4+10,M=1e5+10;
int n,m,ans=0x7fffffff,f[N];
struct edge{int x,y,a,b;inline bool operator<(const edge &s){return a<s.a;}}e[M];
inline int find(int x){return f[x]=(f[x]==x?f[x]:find(f[x]));}

namespace LinkCutTree{
	const int _=N+M;
	int fa[_],ch[_][2],r[_],mx[_],v[_];
	inline int Max(int x,int y){return v[x]<v[y]?y:x;}
	inline void pushup(int x){mx[x]=Max(x,Max(mx[ch[x][0]],mx[ch[x][1]]));}
	inline void pushr(int x){swap(ch[x][0],ch[x][1]);r[x]^=1;}
	inline void pushdown(int x){if(r[x])pushr(ch[x][0]),pushr(ch[x][1]),r[x]=0;}
	inline int getdir(int x){return x==ch[fa[x]][1];}
	inline bool noroot(int x){return x==ch[fa[x]][0]||x==ch[fa[x]][1];}
	inline void rotate(int x){
		int f=fa[x],p=fa[f],k=getdir(x),s=ch[x][k^1];
		ch[x][k^1]=f;ch[f][k]=s;if(noroot(f))ch[p][getdir(f)]=x;
		fa[f]=x;fa[x]=p;if(s)fa[s]=f;
		pushup(f);
	}
	inline void splay(int x){
		static int stk[_];
		int tp=0,y=x;stk[++tp]=y;
		while(noroot(y))stk[++tp]=y=fa[y];
		while(tp)pushdown(stk[tp--]);
		for(int f=fa[x];noroot(x);rotate(x),f=fa[x])
			if(noroot(f))rotate(getdir(f)==getdir(x)?f:x);
		pushup(x);
	}
	inline void access(int x){
		for(int y=0;x;y=x,x=fa[x])
			splay(x),ch[x][1]=y,pushup(x);
	}
	inline void makeroot(int x){
		access(x),splay(x),pushr(x);
	}
	inline void split(int x,int y){
		makeroot(x);access(y);splay(y);
	}
	inline void cut(int x,int y){
		split(x,y);
		fa[x]=ch[y][0]=0;
	}
}using namespace LinkCutTree;

int main(){
	n=read(),m=read();
	REP(i,1,m)e[i]=(edge){read(),read(),read(),read()};
	sort(e+1,e+m+1);
	REP(i,1,n)f[i]=i;
	REP(t,1,m){
		if(e[t].x==e[t].y)continue;
		int x=find(e[t].x),y=find(e[t].y);
		if(x^y){
			f[x]=y;x=e[t].x,y=e[t].y;
			v[n+t]=e[t].b;
			makeroot(x);fa[fa[x]=n+t]=y;
		}
		else{
			x=e[t].x,y=e[t].y;
			split(x,y);int s=mx[y];
			if(v[s]>e[t].b){
				cut(x,s);cut(y,s);
				v[n+t]=e[t].b;
				makeroot(x);fa[fa[x]=n+t]=y;
			}
		}
		if(find(1)==find(n)){
			split(1,n);
			ans=min(ans,e[t].a+e[mx[n]-n].b);
		}
	}
	if(ans<(int)0x7fffffff)printf("%d\n",ans);
	else puts("-1");
	return 0;
}
posted @ 2020-01-01 10:13  Fruitea  阅读(149)  评论(0编辑  收藏  举报