[BZOJ3669] [NOI2004] 魔法森林 LCT维护最小生成树

题面

一开始看到这道题虽然知道是跟LCT维护最小生成树相关的但是没有可以的去想。

感觉可以先二分一下总的精灵数,但是感觉不太好做。

又感觉可以只二分一种精灵,用最小生成树算另一种精灵,但是和似乎不单调。

然后就可以自然地想到先把边按(a)从小到大加入,用LCT维护最小生成树,直接更新答案即可。

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define REP(i,a,n) for(register int i(a);i<=(n);++i)
#define dbg(...) fprintf(stderr,__VA_ARGS__)
#define lc t[o].c[0]
#define rc t[o].c[1]
const int SZ=(1<<21)+1;char ibuf[SZ],*iS,*iT,obuf[SZ+128],*oS=obuf,*oT=obuf+SZ-1;
#ifndef ONLINE_JUDGE
#define gc() getchar()
#else
#define gc() (iS==iT?(iT=(iS=ibuf)+fread(ibuf,1,SZ,stdin),iS==iT?EOF:*iS++):*iS++)
#endif
template<typename I>inline void read(I&x){char c=gc();int f=1;for(;c<'0'||c>'9';c=gc())c=='-'?f=-1:0;for(x=0;c>='0'&&c<='9';c=gc())x=(x<<1)+(x<<3)+(c&15);f==-1?x=-x:0;}
typedef long long ll;typedef unsigned long long ull;
template<typename A,typename B>inline char SMAX(A&a,const B&b){return a<b?a=b,1:0;}
template<typename A,typename B>inline char SMIN(A&a,const B&b){return a>b?a=b,1:0;}

const int N=50000+7,M=1e5+7;
int n,m,ans=0x3f3f3f3f;
struct Edges{int x,y,a,b;inline char operator<(const Edges&x)const{return a<x.a||(a==x.a&&b<x.b);}}e[M];//错误笔记:一定要加const!!!! 

struct Node{int c[2],fa,v,max,pos,rev;}t[N+M];int S[N+M],top;
inline char Isroot(int o){return t[t[o].fa].c[0]!=o&&t[t[o].fa].c[1]!=o;}
inline char Identify(int o){return t[t[o].fa].c[1]==o;}
inline void Connect(int fa,int o,int d){t[fa].c[d]=o;t[o].fa=fa;}
inline void Pushup(int o){t[o].max=t[o].v;t[o].pos=o;if(SMAX(t[o].max,t[lc].max))t[o].pos=t[lc].pos;if(SMAX(t[o].max,t[rc].max))t[o].pos=t[rc].pos;}
inline void Rotate(int o){int fa=t[o].fa,pa=t[fa].fa,d1=Identify(o),d2=Identify(fa),b=t[o].c[d1^1];if(!Isroot(fa))t[pa].c[d2]=o;t[o].fa=pa;Connect(fa,b,d1);Connect(o,fa,d1^1);Pushup(fa),Pushup(o);}
inline void Pushdown(int o){if(t[o].rev){t[o].rev=0;if(lc)t[lc].rev^=1,std::swap(t[lc].c[0],t[lc].c[1]);if(rc)t[rc].rev^=1,std::swap(t[rc].c[0],t[rc].c[1]);}}
inline void Splay(int o){
	int x=o;S[top=1]=x;while(!Isroot(x))S[++top]=x=t[x].fa;while(top)Pushdown(S[top--]);
	while(!Isroot(o)){int fa=t[o].fa;if(Isroot(fa))Rotate(o);else if(Identify(o)==Identify(fa))Rotate(fa),Rotate(o);else Rotate(o),Rotate(o);}
}
inline void Access(int o){for(register int x=0;o;o=t[x=o].fa)Splay(o),rc=x,Pushup(o);}
inline void Makeroot(int o){Access(o);Splay(o);t[o].rev^=1;std::swap(lc,rc);}
inline int Findroot(int o){Access(o);Splay(o);while(lc)Pushdown(o),o=lc;Splay(o);return o;}
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)t[x].fa=y;}
inline void Cut(int x,int y){Split(x,y);if(t[y].c[0]&&!t[x].c[1])t[y].c[0]=t[x].fa=0;Pushup(y);}

int main(){
	read(n),read(m);
	REP(i,1,m)read(e[i].x),read(e[i].y),read(e[i].a),read(e[i].b);
	std::sort(e+1,e+m+1);
	REP(i,1,m){
		const int&x=e[i].x,&y=e[i].y,&a=e[i].a,&b=e[i].b;
		Makeroot(x);Access(y);Splay(x);const int d=t[x].max,p=t[x].pos;
		if(Findroot(x)!=Findroot(y))t[i+n].v=t[i+n].max=b,t[i+n].pos=i+n,Link(e[i].x,i+n),Link(e[i].y,i+n);
		else if(b<d)Cut(p,e[p-n].x),Cut(p,p[e-n].y),t[i+n].v=t[i+n].max=b,t[i+n].pos=i+n,Link(e[i].x,i+n),Link(e[i].y,i+n);
		if(Findroot(1)==Findroot(n))Split(1,n),SMIN(ans,a+t[n].max);
	}printf("%d
",ans==0x3f3f3f3f?-1:ans);
}
原文地址:https://www.cnblogs.com/hankeke/p/9971517.html