5883. 【NOIP2018模拟A组9.25】到不了(启发式合并)

题目大意:

维护一个森林,支持连边操作和查询两点 LCA 操作

思路:

启发式合并,每次把小的树连接到大的树上面就好了,暴力重构lca数组,维护个根,就可以快速得出lca了
还可以离线lct,这个我不是很懂…晚上可以问问dalao们

程序:

#include<cstdio>
#include<iostream>
#include<algorithm>
#define N 100005
using namespace std;

int cnt,n,m,u,v,op,q;
int size[N],f[N][21],last[N],dep[N],rt[N],root1[N];
struct tree{int to,next;}e[N*2];
void add(int x,int y){
	e[++cnt].to=y; e[cnt].next=last[x]; last[x]=cnt;
	e[++cnt].to=x; e[cnt].next=last[y]; last[y]=cnt;
}

void dfs(int x,int fa){
	f[x][0]=fa;
	for (int i=last[x];i;i=e[i].next)
	if (e[i].to!=fa){
		dep[e[i].to]=dep[x]+1;
		dfs(e[i].to,x);
		size[x]+=size[e[i].to];
	}
}

int root(int x){
	for (int i=20;i>=0;i--) if (f[x][i]) x=f[x][i];
	return x;
}

void make(int x,int fa){
	for (int i=1;i<=20;i++) f[x][i]=f[f[x][i-1]][i-1];
	for (int i=last[x];i;i=e[i].next)
	if (e[i].to!=fa){
		dep[e[i].to]=dep[x]+1;
		f[e[i].to][0]=x;
		make(e[i].to,x);
	}
}

int lca(int x,int y){
	if (dep[x]>dep[y]) swap(x,y);
	for (int i=20;i>=0;i--)
		if (dep[f[y][i]]>=dep[x]) y=f[y][i];
	if (x==y) return x;
	for (int i=20;i>=0;i--)
		if (f[y][i]!=f[x][i]) y=f[y][i],x=f[x][i];
	return f[x][0];
}

int main(){
	freopen("arrival.in","r",stdin);
	freopen("arrival.out","w",stdout);
	scanf("%d%d",&n,&m);
	for (int i=1;i<=m;i++) scanf("%d",&rt[i]);
	for (int i=1;i<=n-m;i++){
		scanf("%d%d",&u,&v);
		add(u,v);
	}
	for (int i=1;i<=n;i++) size[i]=1;
	for (int i=1;i<=m;i++) {
		dep[rt[i]]=1;
		dfs(rt[i],0);
	}
	for (int j=1;j<=20;j++)
	 for (int i=1;i<=n;i++)
	  f[i][j]=f[f[i][j-1]][j-1];
	for (int i=1;i<=n;i++){
		int o=root(i);
		if (o) root1[i]=o;
		  else root1[i]=i;
	}
	scanf("%d",&q);
	for (int i=1;i<=q;i++){
		scanf("%d%d%d",&op,&u,&v);
		if (op==1){
			int x=root(u),y=root(v);
			if (x==y) continue;
			if (size[x]>size[y]) {
				f[v][0]=u;
				size[x]+=size[y];
				dep[v]=dep[u]+1;
				root1[x]=root1[x];
				add(u,v);
				make(v,u);
			} else {
				f[u][0]=v;
				size[y]+=size[x];
				dep[u]=dep[v]+1;
				root1[y]=root1[x];
				add(u,v);
				make(u,v);
			}
		} else {
			if (root(u)!=root(v)){
				printf("orzorz\n");
				continue;
			}
			int x=root(u);
			int o=lca(u,v),t=lca(root1[x],v);
			if (dep[o]<dep[t]) o=t;
			t=lca(root1[x],u);
			if (dep[o]<dep[t]) o=t;
			printf("%d\n",o);
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值