BZOJ 4736/UOJ #274. 【清华集训2016】温暖会指引我们前行 LCT边权操作



维护动态最大生成树

最开始YY了一个 线段树分治 kruskal重构树

然后觉得复杂度不对? 不过BJ对kruskal重构树也仅仅算理解 没写过。。

希望有人带带 QWQ 告诉我对不对、怎么做哦~


LCT维护最大生成树

加入一条边时

若两点未联通 直接加

否则找到两点路径上最小的边 看能不能替换掉


#include<cmath>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<string>
#include<bitset>
#include<queue>
#include<set>
#include<map>
using namespace std;

inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
void print(int x)
{if(x<0)putchar('-'),x=-x;if(x>=10)print(x/10);putchar(x%10+'0');}

const int N=400100,inf=0X3f3f3f3f;

int n;

int fa[N],ch[N][2];
int mn[N],sum[N],tim[N],len[N];
bool rev[N];

inline bool isroot(int x)
{return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}

inline void pushup(int x)
{
	mn[x]=x;
	int ls=ch[x][0],rs=ch[x][1];
	if(ls && tim[mn[ls]]<tim[mn[x]]) mn[x]=mn[ls];
	if(rs && tim[mn[rs]]<tim[mn[x]]) mn[x]=mn[rs];
	sum[x]=sum[rs]+sum[ls]+len[x];
}

inline void pushdown(int x)
{
	if(rev[x])
	{
		rev[x]=0;
		if(ch[x][0]) rev[ch[x][0]]^=1;
		if(ch[x][1]) rev[ch[x][1]]^=1;
		swap(ch[x][0],ch[x][1]);
	}
}

void getdown(int x)
{if(!isroot(x))getdown(fa[x]);pushdown(x);}

inline void rotate(int x)
{
	int y=fa[x],z=fa[y],l,r;
	l=(ch[y][1]==x);r=l^1;
	if(!isroot(y)) ch[z][ch[z][1]==y]=x;
	fa[x]=z;fa[y]=x;fa[ch[x][r]]=y;
	ch[y][l]=ch[x][r];ch[x][r]=y;
	pushup(y);pushup(x);
}

void splay(int x)
{
	getdown(x);
	int y,z;
	while(!isroot(x))
	{
		y=fa[x];z=fa[y];
		if(!isroot(y))
		{
			if((ch[z][0]==y)^(ch[y][0]==x)) rotate(x);
			else rotate(y);
		}
		rotate(x);
	}
}

void access(int x)
{int t=0;while(x){splay(x);ch[x][1]=t;pushup(x);t=x;x=fa[x];}}

void rever(int x)
{access(x);splay(x);rev[x]^=1;}

int find(int x)
{access(x);splay(x);while(ch[x][0])x=ch[x][0];splay(x);return x;}

void split(int u,int v)
{rever(u);access(v);splay(v);}

void link(int u,int v)
{rever(u);fa[u]=v;}

void cut(int u,int v)
{split(u,v);fa[u]=0;ch[v][0]=0;pushup(u);pushup(v);}

int query_sum(int u,int v)
{split(u,v);return sum[v];}

int U[N],V[N];

int main()
{
	n=read();int Q=read();
	register int i,j,u,v,pos,tmp;
	for(i=1;i<=n;++i) tim[i]=inf,mn[i]=i;
	char opt[10];
	while(Q--)
	{
		scanf("%s",opt);
		switch(opt[0])
		{
			case 'f':
				pos=read()+n+1;
				u=U[pos]=read()+1;v=V[pos]=read()+1;
				mn[pos]=pos;
				tim[pos]=read();sum[pos]=len[pos]=read();
				if(find(u)!=find(v))
					link(u,pos),link(v,pos);
				else
				{
					split(u,v);
					tmp=mn[v];i=U[tmp];j=V[tmp];
					if(tim[tmp]<tim[pos])
						cut(tmp,i),cut(tmp,j),
						link(pos,u),link(pos,v);
				}
				break;
			case 'm':
				u=read()+1;v=read()+1;
				if(find(u)==find(v))
					print(query_sum(u,v)),puts("");
				else puts("-1");
				break;
			case 'c':
				pos=read()+n+1;
				splay(pos);
				len[pos]=read();
				pushup(pos);
				break;
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值