LCT(动态最小生成树)+kruscal--bzoj2594: [Wc2006]水管局长数据加强版

传送门
(好久不打lct板子都快忘了

lct维护边权的套路就是把边拆成点,这个点向两边的点分别连边,点权就是边权,原树上的点根据需要可以设点权为inf或者-inf

动态最小生成树离线删边变加边,就是在 l c t lct lct中记录 m x [ x ] mx[x] mx[x] i d [ x ] id[x] id[x]表示子树点权中最大的点权和点的编号,加入一条新边 ( x , y ) (x,y) (x,y)的时候,如果两点没有联通就连起来,如果已经联通就 s p l i t ( x , y ) split(x,y) split(x,y),看最大的点权是不是比新加的点权大,大的话就把它原图中两边的点都 c u t cut cut掉然后 l i n k link link上这条边

话说第一回开大数组还 M L E MLE MLE了一次QAQ

还有第一次可以用并查集 k r u s c a l kruscal kruscal做!并查集常数比 l c t lct lct不知道优秀多少,本地(机子比较慢)最大的数据点用 l c t lct lct做跑了 13 s 13s 13s,但是换成并查集的 k r u s c a l kruscal kruscal就只跑了 4 s 4s 4s

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<map>
#define inf 0x3f3f3f3f
#define N 1100005
#define M 1000005
#define maxn 100005
#define ls ch[x][0]
#define rs ch[x][1]
#define LL long long
using namespace std;

template<class T>inline void rd(T &x){
	x=0; short f=1; char c=getchar();
	while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
	while(c<='9' && c>='0') x=x*10+(c^'0'),c=getchar();
	x*=f;
}

namespace lct{
	int ch[N][2],f[N],rev[N],mx[N],id[N],w[N];
	inline int get(int x){return ch[f[x]][1]==x;}
	inline void update(int x){
		mx[x]=w[x],id[x]=x;
		if(ls || rs){
			mx[x]=w[x],id[x]=x;
			if(mx[x]<mx[ls]) mx[x]=mx[ls],id[x]=id[ls];
			if(mx[x]<mx[rs]) mx[x]=mx[rs],id[x]=id[rs];
		}
	}
	inline int isroot(int x){return ch[f[x]][0]!=x && ch[f[x]][1]!=x;}
	inline void rever(int x){rev[x]^=1;swap(ls,rs);}
	inline void pushdown(int x){
		if(rev[x]){
			if(ls) rever(ls);
			if(rs) rever(rs);
			rev[x]=0;
		}
	}
	void pushup(int x){if(!isroot(x))pushup(f[x]);pushdown(x);}
	inline void rotate(int x){
		int wh=get(x),old=f[x],oldf=f[old];
		if(!isroot(old)) ch[oldf][get(old)]=x;
		ch[old][wh]=ch[x][wh^1],f[ch[x][wh^1]]=old;
		f[old]=x,ch[x][wh^1]=old,f[x]=oldf;
		update(old); update(x); 
	}
	inline void splay(int x){
		pushup(x);
		for(;!isroot(x);rotate(x))
			if(!isroot(f[x])) rotate(get(f[x])==get(x)?f[x]:x);	
	}
	inline void access(int x){
		for(int y=0;x;y=x,x=f[x]) splay(x),rs=y,update(x);
	}
	inline void makeroot(int x){
		access(x); splay(x); rever(x);
	}
	inline int findroot(int x){
		access(x); splay(x);
		while(ls) pushdown(x),x=ls; return x;
	}
	inline void split(int x,int y){
		makeroot(x); access(y); splay(y);
	}
	inline void link(int x,int y){
		makeroot(x); f[x]=y;
	}
	inline void cut(int x,int y){
		split(x,y);
		if(ch[y][0]==x && ch[x][1]==0)
			f[x]=ch[y][0]=0,update(y);
	}
}

int n,m,Q,q[maxn][3],tot,fr2[M],to2[M],ans[maxn],val[maxn],fa[maxn];
map<pair<int,int>,int> mp;

inline int find(int x){
	return x==fa[x]?x:(fa[x]=find(fa[x]));
}

struct EDGE{
	int fr,to,w;
	bool operator <(const EDGE &x) const{
		return w<x.w;
	}
}edge[M];

int main(){
	rd(n); rd(m); rd(Q);
	for(int i=1;i<=m;i++){
		rd(edge[i].fr); rd(edge[i].to); rd(edge[i].w);
	}
	for(int i=1;i<=Q;i++){
		rd(q[i][0]); rd(q[i][1]); rd(q[i][2]);
		if(q[i][0]==2)
			mp[make_pair(q[i][1],q[i][2])]=mp[make_pair(q[i][2],q[i][1])]=i;
	}
	tot=n;
	for(int i=1;i<=n;i++) lct::w[i]=-inf,fa[i]=i;
	sort(edge+1,edge+m+1);
	for(int i=1;i<=m;i++)
		if(!mp.count(make_pair(edge[i].fr,edge[i].to))){
			int x=edge[i].fr,y=edge[i].to;
			int u=find(x),v=find(y);
			if(u!=v){
				lct::w[++tot]=edge[i].w; fr2[tot]=x,to2[tot]=y;
				lct::link(x,tot),lct::link(y,tot); fa[u]=v;
			}
//			else{
//				lct::split(x,y);
//				if(lct::mx[y]>w[i]){
//					int now=lct::id[y];
//					lct::cut(fr2[now],now),lct::cut(to2[now],now);
//					lct::w[++tot]=w[i]; fr2[tot]=x,to2[tot]=y;
//					lct::link(x,tot),lct::link(y,tot);
//				}
//			}
		}
		else val[mp[make_pair(edge[i].fr,edge[i].to)]]=edge[i].w;
	for(int i=Q;i;i--){
		if(q[i][0]==1){
			int x=q[i][1],y=q[i][2];
			lct::split(x,y);
			ans[i]=lct::mx[y];
		}
		else{
			int x=q[i][1],y=q[i][2],z=val[i];
			if(lct::findroot(x)!=lct::findroot(y)){
				lct::w[++tot]=z; fr2[tot]=x,to2[tot]=y;
				lct::link(x,tot),lct::link(y,tot);
			}
			else{
				lct::split(x,y);
				if(lct::mx[y]>z){
					int now=lct::id[y];
					lct::cut(fr2[now],now),lct::cut(to2[now],now);
					lct::w[++tot]=z; fr2[tot]=x,to2[tot]=y;
					lct::link(x,tot),lct::link(y,tot);
				}
			}
		}
	}
	for(int i=1;i<=Q;i++) 
		if(q[i][0]==1) printf("%d\n",ans[i]);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值