LCT

推荐:https://www.cnblogs.com/flashhu/p/8324551.html

分析

用来处理有删边但没有子树操作的问题

模板

inline bool nrt(int x) { //判断x是否为splay中的根,认父不认子
    return c[f[x]][0]==x||c[f[x]][1]==x;
}
void rotate(int x) {
    int y=f[x],z=f[y],k=c[y][1]==x;
    if(nrt(y)) c[z][c[z][1]==y]=x; f[x]=z;
    if(c[x][!k]) f[c[x][!k]]=y; c[y][k]=c[x][!k];
    c[x][!k]=y,f[y]=x;
    up(y);
}
void splay(int x) {
    int y=x,z=0;
    st[++z]=x;
    while(nrt(y)) st[++z]=f[y],y=f[y];
    while(z) down(st[z--]); 
    while(nrt(x)) {
        int y=f[x],z=f[y];
        if(nrt(y)) {
            rotate((c[y][0]==x)^(c[z][0]==y)?x:y);
        }
        rotate(x);
    }
    up(x);
}

access:打通x到根的道路,splay中的键值是深度

void access(int x) {
    for(int y=0;x;y=x,x=f[x]) {
        splay(x),c[x][1]=y,f[y]=x,up(x);
    }
}

makeroot:将x变成根

inline void pushr(int x) {
    swap(c[x][0],c[x][1]);
    rev[x]^=1;
}
void makert(int x) {
    access(x),splay(x);
    pushr(x);
}

split:将x,y路径提到一个splay中,以y为根

void split(int x,int y) {
    makert(x),access(y),splay(y);
}

*findroot:寻找x的根(树上)

int findrt(int x) {
    access(x),splay(x);
    for(;c[x][0];x=c[x][0]) down(x);
    splay(x); //保证时间复杂度
    return x;
}

link:连接

void link(int x,int y) {
    makert(x);
    *if(findrt(y)==x) return;
    f[x]=y;
}

cut:删除

void cut(int x,int y) {
    makert(x),access(y),splay(y);
    *if(c[y][0]!=x||c[x][1]||f[x]!=y) return;
    f[x]=0,c[y][0]=0; up(y);
}

基槽1

可以动态维护最小(大)生成树

例题1

[Luogu P2387 [NOI2014] 魔法森林](http://www.cnblogs.com/ https://www.luogu.com.cn/problem/P2387)

按a排序后动态维护最小生成树即可,裂点成边

#include<bits/stdc++.h>
const int INF=1e9;
using namespace std;

const int N=2e5+5;
int n,m; 
struct A{int u,v,a,b; }E[N];
inline bool cmp(A i,A j) {
	return i.a<j.a;
}
namespace LCT {
	#define ls(x) ch[x][0]
	#define rs(x) ch[x][1]	
	int ch[N][2],c[N],s[N],f[N],cnt,r[N],a[N],st[N];
	inline void pushr(int u) {
		swap(ls(u),rs(u));
		r[u]^=1;
	}
	inline void down(int u) {
		if(r[u]) {
			if(ls(u)) pushr(ls(u));
			if(rs(u)) pushr(rs(u));
			r[u]=0; 
		}
	}
	inline void up(int u) {
		s[u]=a[u]; c[u]=u;
		if(ls(u)&&s[ls(u)]>s[u]) {
			s[u]=s[ls(u)];
			c[u]=c[ls(u)];
		}
		if(rs(u)&&s[rs(u)]>s[u]) {
			s[u]=s[rs(u)];
			c[u]=c[rs(u)];
		}
	}
	inline bool nrt(int x) {
		return ls(f[x])==x||rs(f[x])==x;
	}
	void rotate(int x) {
		int y=f[x],z=f[y]; bool k=(rs(y)==x);
		if(nrt(y)) ch[z][rs(z)==y]=x; f[x]=z;
		if(ch[x][!k]) f[ch[x][!k]]=y; ch[y][k]=ch[x][!k];
		ch[x][!k]=y; f[y]=x;
		up(y);
	}
	void splay(int x) {
		int y=x,z=0;
		st[++z]=y;
		for(;nrt(y);y=f[y]) st[++z]=f[y];
		for(;z;z--) down(st[z]);
		while(nrt(x)) {
			y=f[x],z=f[y];
			if(nrt(y)) rotate(((rs(y)==x)^(rs(z)==y))?x:y);
			rotate(x);
		}
		up(x);
	}
	void access(int x) {
		for(int y=0;x;y=x,x=f[x]) {
			splay(x),rs(x)=y,up(x);
		}
	}
	void makert(int u) {
		access(u),splay(u);
		pushr(u);
	}
	void link(int u,int v,int k) {
		makert(u);
		s[++cnt]=k,a[cnt]=k,c[cnt]=cnt;
		f[u]=cnt,f[cnt]=v;
	}
	int find(int u) {
		access(u),splay(u);
		for(;ls(u);u=ls(u)) down(u);
		splay(u);
		return u;
	}
	void split(int u,int v) {
		makert(u); access(v),splay(v);
	}
	void cut(int u) {
		makert(u),down(u);
		f[ls(u)]=f[rs(u)]=0;
	}
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++) {
		scanf("%d%d%d%d",&E[i].u,&E[i].v,&E[i].a,&E[i].b); 
	}
	sort(E+1,E+m+1,cmp);
	int ans=INF; LCT::cnt=n;
	for(int i=1;i<=m;i++) {
		int u=E[i].u,v=E[i].v;
		int t1=LCT::find(u),t2=LCT::find(v);
		if(t1!=t2) {
			LCT::link(u,v,E[i].b);
		} else {
			LCT::split(u,v);
			if(LCT::s[v]>E[i].b) {
				LCT::cut(LCT::c[v]);
				LCT::link(u,v,E[i].b);
			}
		}
		if(LCT::find(1)==LCT::find(n)) {
			LCT::split(1,n);
			ans=min(ans,E[i].a+LCT::s[n]);
		}
	}
	printf("%d\n",ans==INF?-1:ans);
	return 0;
}

例题2

Luogu P4234 最小差值生成树

枚举最大值,然后维护最大生成树

注意:multiset的erase会删掉全部

#include<bits/stdc++.h>
const int INF=1e9;
using namespace std;

const int N=1e6+5;
int c[N][2],n,m,s[N],loc[N],st[N],f[N],rev[N],a[N];
int tot;

struct A{int x,y; };
vector<A>t[N];

inline bool nrt(int x) {
    return c[f[x]][0]==x|c[f[x]][1]==x;
}

inline void R(int x) {
    swap(c[x][0],c[x][1]);
    rev[x]^=1;
}

inline void down(int x) {
    if(rev[x]) {
        if(c[x][0]) R(c[x][0]);
        if(c[x][1]) R(c[x][1]);
        rev[x]=0;
    }
}

inline void up(int x) {
    s[x]=max(max(s[c[x][0]],s[c[x][1]]),a[x]);
    if(a[x]==s[x]) loc[x]=x;
        else if(s[x]==s[c[x][0]]) loc[x]=loc[c[x][0]];
            else loc[x]=loc[c[x][1]];
}

void rotate(int x) {
    int y=f[x],z=f[y],k=c[y][1]==x;
    if(nrt(y)) c[z][c[z][1]==y]=x; f[x]=z;
    if(c[x][!k]) f[c[x][!k]]=y; c[y][k]=c[x][!k];
    c[x][!k]=y,f[y]=x;
    up(y);
}

void splay(int x) {
    int y=x,z=0;
    st[++z]=y;
    while(nrt(y)) st[++z]=f[y],y=f[y];
    while(z) down(st[z--]);
    while(nrt(x)) {
        y=f[x],z=f[y];
        if(nrt(y)) {
            rotate((c[y][0]==x)^(c[z][0]==y)?x:y);
        }
        rotate(x);
    }
    up(x);
}

void access(int x) {
    for(int y=0;x;y=x,x=f[x]) {
        splay(x),c[x][1]=y,up(x);
    }
}

void makert(int x) {
    access(x),splay(x);
    R(x);
}

void split(int x,int y) {
    makert(x),access(y),splay(y);
}

void link(int x,int y,int k) {
    makert(x);
    f[x]=++tot,f[tot]=y;
    s[tot]=a[tot]=k,loc[tot]=tot;    
}

void cut(int x) {
    makert(x); down(x);
    f[c[x][0]]=0,f[c[x][1]]=0;
}

int find(int x) {
    access(x),splay(x);
    while(c[x][0]) down(x=c[x][0]);
    splay(x);
    return x;
}
multiset<int>S;
int main() {
    scanf("%d%d",&n,&m); int mx=0;
    for(int i=1;i<=m;i++) {
        int x,y,a; scanf("%d%d%d",&x,&y,&a);
        if(x!=y) t[a].push_back((A){x,y}),mx=max(mx,a);
    } 
    int ans=INF; tot=n; 
    for(int i=mx;i;i--) {
        if(!t[i].empty()) {
            for(int j=0;j<t[i].size();j++) {
                int x=t[i][j].x,y=t[i][j].y;
                if(find(x)==find(y)) {
                    split(x,y);
                    if(s[y]!=i) {
                    	multiset<int>::iterator it;
                    	it=S.
						lower_bound(s[y]);
  	                	S.erase(it),cut(loc[y]);
    	                S.insert(i),link(x,y,i);
        			}
		        } else S.insert(i),link(x,y,i);
            }
        	if(S.size()==n-1) {
           		multiset<int>::iterator it;
           		it=S.end(); it--;
           		ans=min(ans,(*it)-i);
			} 
        }
    }
    printf("%d\n",ans==INF?-1:ans);
    return 0;
}

例题3

ybtoj #731. 「动态树」图上询问

联通块数量=\(n-\)生成树边数

不妨将询问按\(r\)排序,然后维护以编号为边权的最大生成树

考虑如何求出答案,可以用树状数组维护最大生成树上的编号情况

#include<bits/stdc++.h>
const int INF=1e9;
using namespace std;

const int N=2e5+5,M=4e5+5;
int n,m,Q,co[M],c[M][2],rev[M],a[M],f[M],mn[M],st[M],ans[N],E1[N],E2[N];

struct A{int a,b; };
vector<A>q[N];

void add(int x,int k) {
	for(int i=x;i;i-=i&-i) co[i]+=k;
}

int ask(int x) {
	int ret=0;
	for(int i=x;i<=n+m;i+=i&-i) ret+=co[i];
	return ret;	
}
inline void pushr(int x) {
	swap(c[x][0],c[x][1]);
	rev[x]^=1;
}

inline bool nrt(int x) {
	return c[f[x]][0]==x||c[f[x]][1]==x;
}
inline void up(int x) {
	mn[x]=a[x];
	if(c[x][0]) mn[x]=min(mn[x],mn[c[x][0]]);
	if(c[x][1]) mn[x]=min(mn[x],mn[c[x][1]]);
}

inline void down(int x) {
	if(rev[x]) {
		if(c[x][0]) pushr(c[x][0]);
		if(c[x][1]) pushr(c[x][1]);
		rev[x]=0;
	}
}
void rotate(int x) {
	int y=f[x],z=f[y],k=c[y][1]==x;
	if(nrt(y)) c[z][c[z][1]==y]=x; f[x]=z;
	if(c[x][!k]) f[c[x][!k]]=y; c[y][k]=c[x][!k];
	c[x][!k]=y,f[y]=x;
	up(y);
}

void splay(int x) {
	int y=x,z=0; st[++z]=x;
	while(nrt(y)) st[++z]=f[y],y=f[y];
	while(z) down(st[z]),z--;
	while(nrt(x)) {
		y=f[x],z=f[y];
		if(nrt(y)) {
			rotate((c[y][0]==x)^(c[z][0]==y)?x:y);
		}
		rotate(x);
	}
	up(x);
}

void access(int x) {
	for(int y=0;x;y=x,x=f[x]) {
		splay(x); c[x][1]=y; up(x);
	}
}
void makert(int x) {
	access(x),splay(x);
	pushr(x);
}

void split(int x,int y) {
	makert(x); access(y),splay(y); 
}

void cut(int x,int y) {
	split(x,y);
	f[x]=c[y][0]=0; 
	up(y); 
}

int findrt(int x) {
	access(x),splay(x);
	down(x);
	while(c[x][0]) x=c[x][0],down(x);
	splay(x);
	return x;
}

void link(int x,int y,int i) {
	int New=i+n; a[New]=i;
	if(findrt(x)==findrt(y)) {
		split(x,y);
		int t=mn[y];
		add(t,-1);
		cut(E1[t],t+n),cut(t+n,E2[t]);
	}
	makert(x); f[x]=New; 
	makert(New); f[New]=y;
	add(a[New],1);
}

int main() {
	freopen("graph.in","r",stdin);
	freopen("graph.out","w",stdout);
	scanf("%d%d%d",&n,&m,&Q);
	for(int i=1;i<=n;i++) a[i]=INF;
	for(int i=1;i<=m;i++) {
		scanf("%d%d",&E1[i],&E2[i]);
	}
	for(int i=1;i<=Q;i++) {
		int u,v; scanf("%d%d",&u,&v);
		q[v].push_back((A){u,i});
	}
	for(int i=1;i<=m;i++) {
		int u=E1[i],v=E2[i];
		if(u!=v) link(u,v,i);
		for(auto t:q[i]) {
			ans[t.b]=n-ask(t.a);
		}	
	}
	for(int i=1;i<=Q;i++) {
		printf("%d\n",ans[i]);
	}
	return 0;
}
posted @ 2021-04-09 08:14  wwwsfff  阅读(137)  评论(0)    收藏  举报