全局平衡二叉树

全局平衡二叉树

类似于静态的 LCT?

建树方法:

先树剖,考虑对于每一条重链维护一棵二叉树,且每条重链的二叉树的根与该重链的链头的父亲之间有一条虚边(认父不认子)。

为了达到全局的平衡,每棵重链的二叉树并不是完美的二叉树:对于该重链上的每个点 u u u 设置一个权值 v u v_u vu 为该点的轻子树大小和 + 1 +1 +1(即 s z [ u ] − s z [ s o n [ u ] ] sz[u]-sz[son[u]] sz[u]sz[son[u]]),然后找到这条链上的带权中点作为这棵二叉树的根(即找到一个位置 k k k 使得 s k − 1 ≤ s u m 2 < s k s_{k-1}\leq \frac{sum}{2}< s_{k} sk12sum<sk,其中 s s s 是前缀和(注意前缀和均大于 0 0 0 且严格单增), s u m sum sum 是总和),然后再对两边递归建树。

这么建出来的树的树高是 O ( log ⁡ n ) O(\log n) O(logn) 的,证明:

考虑在全局平衡二叉树中任意一个点到全局的根经过的边的数量。对于虚实边分开考虑:对于虚边,根据树剖可知从任意一个点到全局的根至多经过 log ⁡ n \log n logn 条虚边;对于实边,我们设 n s ns ns 表示当前点在全局平衡二叉树中的子树大小(算上虚子树),那么根据我们建立全局平衡二叉树的方式,可知从当前点走一条实边跳到父亲后 n s ns ns 至少乘 2 2 2,于是实边也至多只会经过 log ⁡ n \log n logn 条。于是总共的树高也不会超过 2 log ⁡ n 2\log n 2logn

全局平衡二叉树能以优秀的复杂度和常数处理很多问题。

一个最基础的问题就是树上带修路径 max ⁡ \max max。还是按照类似树剖的做法在全局平衡二叉树上不断往上跳。对于经过的一条非 l c a lca lca 所在重链的重链,我们要求的是它的一段前缀,那么我们边跳边统计左子树的信息即可,这部分的时间复杂度为 O ( log ⁡ n ) O(\log n) O(logn)。对于 l c a lca lca 所在重链,我们要求的是该重链上一段区间的信息,这个区间在全局平衡二叉树上对应不超过 O ( log ⁡ n ) O(\log n) O(logn) 个节点,因为全局平衡二叉树树高也就 O ( log ⁡ n ) O(\log n) O(logn)

另一个问题是维护子树信息。类似于 LCT 维护子树信息,我们对全局平衡二叉树上每个点记录其虚子树的信息和以及它在全局平衡二叉树上的整棵子树的信息和。然后询问某棵子树的时候我们直接找到该子树的根所在重链,然后相当于询问这条重链的一个后缀的节点信息和,这同样只对应了 O ( log ⁡ n ) O(\log n) O(logn) 个节点。

P4719 【模板】“动态 DP”&动态树分治

模板,维护子树的 DP 信息( u u u 的 DP 值由儿子的 DP 值转移得来,然后我们要维护的就是每个节点虚儿子的 DP 值之和以及所有儿子的 DP 值之和(即当前节点的 DP 值))。

#include<bits/stdc++.h>

#define N 1000010
#define INF 1000000000

using namespace std;

inline void upmax(int &x,int y){if(y>x)x=y;}

namespace IO{
    char buf[1000010],*cur=buf+1000010;
    inline char getc(){
        (cur==buf+1000010)?fread(cur=buf,1,1000010,stdin):0;
        return *cur++;
    }
    char buff[1000010],*curr=buff;
    inline void flush(){
        fwrite(buff,1,curr-buff,stdout);
    }
    inline void putc(const char &ch){
        (curr==buff+1000010)?fwrite(curr=buff,1,1000010,stdout):0;
        *curr++=ch;
    }  
    inline void rd(int &x){
        x=0;char ch=getc();bool neg=0;
        while(ch<'0'||ch>'9')
		{
			if(ch=='-') neg=1;
			ch=getc();
		}
        while(ch>='0'&&ch<='9'){
            x=(x<<1)+(x<<3)+(ch^'0');
            ch=getc();
        }
        if(neg) x=-x;
    }
    char st[60];int tp;
    void PT(int x){
    	if(x<0) putc('-'),x=-x;
        if(x==0)putc('0');
        else{
            while(x>0){
                st[++tp]=x%10+'0';
                x/=10;
            }
        }
        while(tp)putc(st[tp--]);
    }
}using IO::putc;using IO::rd;using IO::PT;

struct Matrix
{
	int a[2][2];
	Matrix(){a[0][0]=a[0][1]=a[1][0]=a[1][1]=-INF;}
	Matrix(int a00,int a01,int a10,int a11){a[0][0]=a00,a[0][1]=a01,a[1][0]=a10,a[1][1]=a11;}
	void unit(){a[0][0]=a[1][1]=0,a[0][1]=a[1][0]=-INF;}
};

Matrix mul(const Matrix &a,const Matrix &b)
{
	Matrix c;
	c.a[0][0]=max(a.a[0][0]+b.a[0][0],a.a[0][1]+b.a[1][0]);
	c.a[0][1]=max(a.a[0][0]+b.a[0][1],a.a[0][1]+b.a[1][1]);
	c.a[1][0]=max(a.a[1][0]+b.a[0][0],a.a[1][1]+b.a[1][0]);
	c.a[1][1]=max(a.a[1][0]+b.a[0][1],a.a[1][1]+b.a[1][1]);
	return c;
}

int n,m,a[N];

namespace BST
{
	#define lc(u) ch[u][0]
	#define rc(u) ch[u][1]
	int ch[N][2],fa[N];
	Matrix sum[N],val[N];
	void up(int u){sum[u]=mul(mul(sum[lc(u)],val[u]),sum[rc(u)]);}
	namespace Build
	{
		vector<int> p,sv;
		vector<Matrix> g;
		int build(int l,int r,int f)
		{
			if(l>r) return 0;
			int half=(sv[r]+(l?sv[l-1]:0))>>1;
			for(int k=l;k<=r;k++)
			{
				if(sv[k]>half)
				{
					int u=p[k];
					fa[u]=f,val[u]=g[k];
					lc(u)=build(l,k-1,u);
					rc(u)=build(k+1,r,u);
					up(u); return u;
				}
			}
		}
		int build(vector<int> &_p,vector<int> &_sv,vector<Matrix> &_g,int fart)
		{
			p.swap(_p),sv.swap(_sv),g.swap(_g);
			return build(0,(int)p.size()-1,fart);
		}
	}using Build::build;
	bool notroot(int u){return lc(fa[u])==u||rc(fa[u])==u;}
	void update(int u,int y)
	{
		val[u].a[1][0]+=y-a[u],a[u]=y;
		while(u)
		{
			if(!notroot(u)&&fa[u])
			{
				int f0=sum[u].a[0][0],f1=sum[u].a[1][0];
				val[fa[u]].a[0][0]-=max(f0,f1),val[fa[u]].a[0][1]-=max(f0,f1),val[fa[u]].a[1][0]-=f0;
				up(u); f0=sum[u].a[0][0],f1=sum[u].a[1][0];
				val[fa[u]].a[0][0]+=max(f0,f1),val[fa[u]].a[0][1]+=max(f0,f1),val[fa[u]].a[1][0]+=f0;
			}
			else up(u);
			u=fa[u];
		}
	}
	#undef lc
	#undef rc
}

namespace Tree
{
	int fa[N],size[N],son[N];
	int cnt,head[N],nxt[N<<1],to[N<<1];
	void adde(int u,int v)
	{
		to[++cnt]=v;
		nxt[cnt]=head[u];
		head[u]=cnt;
	}
	void dfs(int u)
	{
		size[u]=1;
		for(int i=head[u];i;i=nxt[i])
		{
			int v=to[i];
			if(v==fa[u]) continue;
			fa[v]=u,dfs(v);
			size[u]+=size[v];
			if(size[v]>size[son[u]]) son[u]=v;
		}
	}
	int dfs1(int rt)
	{
		vector<int> p,sv;
		vector<Matrix> g;
		for(int u=rt;u;u=son[u])
		{
			int s0=0,s1=0;
			for(int i=head[u];i;i=nxt[i])
			{
				int v=to[i];
				if(v==fa[u]||v==son[u]) continue;
				int vrt=dfs1(v),fv0=BST::sum[vrt].a[0][0],fv1=BST::sum[vrt].a[1][0];
				s0+=fv0,s1+=max(fv0,fv1);
			}
			p.push_back(u),sv.push_back(size[u]-size[son[u]]);
			if(son[u]) g.push_back(Matrix(s1,s1,a[u]+s0,-INF));
			else g.push_back(Matrix(0,-INF,a[u],-INF));
		}
		for(int i=1;i<(int)sv.size();i++) sv[i]+=sv[i-1];
		return BST::build(p,sv,g,fa[rt]);
	}
	int init(){dfs(1);return dfs1(1);}
}

int main()
{
	BST::sum[0].unit();
	rd(n),rd(m);
	for(int i=1;i<=n;i++) rd(a[i]);
	for(int i=1;i<n;i++)
	{
		int u,v;rd(u),rd(v);
		Tree::adde(u,v),Tree::adde(v,u);
	}
	int rt=Tree::init(),lans=0;
	while(m--)
	{
		int x,y;rd(x),rd(y),x^=lans;
		BST::update(x,y);
		lans=max(BST::sum[rt].a[0][0],BST::sum[rt].a[1][0]);
		PT(lans),putc('\n');
	}
	IO::flush();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值