2018.12.12【BZOJ5192】【洛谷P4271】New Barns(动态维护倍增数组)

BZOJ传送门

洛谷传送门


解析:

本来这道题ldw说是什么动态点分治?

然后被我成功用动态维护LCA水过去了。

思路:

一看直径,就是最远距离,但是这个显然用 O ( n ) O(n) O(n)的DP每次来一下是不行的。

那么重点就在距离上面,树上距离可以用什么东西维护?LCA+深度就可以 O ( log ⁡ n ) O(\log n) O(logn)算出两点距离。

所以我们只需要维护每棵树的当前直径的左右端点,新加入一个点的时候处理出它的倍增数组,然后更新当前连通块的直径左右端点就行了,新的直径的两个端点必然有至少一个是原来直径的端点。这个证明十分显然,就不讨论了。

对于询问,直接查连通块里面到两个直径的距离就行了。


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

namespace IO{
	inline int getint(){
		re int num;
		re char c;
		re bool f=0;
		while(!isdigit(c=gc()))if(c=='-')f=1;num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return f?-num:num;
	}
	
	inline void outint(int a){
		static char ch[23];
		if(a==0)pc('0');
		while(a)ch[++ch[0]]=a-a/10*10,a/=10;
		while(ch[0])pc(ch[ch[0]--]^48);
	}
	
	inline char getalpha(){
		re char c;
		while(!isalpha(c=gc()));
		return c;
	}
}
using namespace IO;

cs int N=100005,logN=17;
int block[N],l[N],r[N],bcnt;
int fa[N][logN+1],dep[N]={-1};

inline int dist(int u,int v){
	re int ans=0;
	if(dep[u]>dep[v])swap(u,v);
	
	if(dep[u]<dep[v])
	for(int re i=logN;~i;--i){
		if(dep[fa[v][i]]>=dep[u]){
			v=fa[v][i];
			ans+=1<<i;
		}
	}
	if(v==u)return ans;
	
	for(int re i=logN;~i;--i){
		if(fa[v][i]^fa[u][i]){
			ans+=2<<i;
			u=fa[u][i];
			v=fa[v][i];
		}
	}
	return ans+2;
}

int Q,tot;
signed main(){
	Q=getint();
	while(Q--){
		re char op=getalpha();
		re int u=getint();
		switch(op){
			case 'Q':{
				int b=block[u];
				int d1=dist(u,l[b]),d2=dist(u,r[b]);
				outint(max(d1,d2));
				pc('\n');
				break;
			}
			case 'B':{
				++tot;
				if(~u){
					block[tot]=block[u];
					dep[tot]=dep[u]+1;
					fa[tot][0]=u;
					for(int re i=1;i<=logN;++i)
					fa[tot][i]=fa[fa[tot][i-1]][i-1];
					int b=block[u],d1=dist(tot,l[b]),d2=dist(l[b],r[b]);
					if(d1>d2)r[b]=tot;
					d1=dist(tot,r[b]);
					if(d1>d2)l[b]=tot;
				}
				else{
					block[tot]=++bcnt;
					r[bcnt]=l[bcnt]=tot;
				}
				break;
			}
		}
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值