树链剖分解析—WYD

树链剖分解析—WYD首先,树链剖分是个什么东西?给出一道例题:维护两个操作:1,修改单点值;2,求x~y路径的权值和;点数<=100000,操作数量&a

首先,树链剖分是个什么东西?
给出一道例题:维护两个操作:
1,修改单点值;2,求x~y路径的权值和;
点数<=100000,操作数量<=100000。
考虑LCA?不行,LCA不能支持修改,极限复杂度(N*N),于是…爆炸图片
接下来,引出数据结构—–树链剖分。

定义与概念:(划重点)
重儿子:父亲节点的所有儿子中子树结点数目最多(size最大)的结点;
轻儿子:父亲节点中除了重儿子以外的儿子;
重边:父亲结点和重儿子连成的边;
轻边:父亲节点和轻儿子连成的边;
重链:由多条重边连接而成的路径;
轻链:由多条轻边连接而成的路径;

请花至少五分钟理解之,上图:
这里写图片描述

进行变量声明:
size[v]代表以v为根的子树的节点数
dep[v]代表v的深度(根深度为1)
top[v]代表v所在的链顶顶端节点的编号
fa[v]代表v的父亲,son[v]代表v的重儿子。
tree[v]代表节点v在线段树(数据结构)中的编号(dfs序)
pre[v]代表线段树(数据结构)中编号为v的节点所对应的原图中的点编号(tree[v]与pre[v]是互逆的,tree[2]=3,则pre[3]=2.)
开始树链剖分!

Top1:dg1
此递归可以一举求出size,dep,fa与son。
其中dep[f[k][1]]=dep[t]+1,fa[f[k][1]]=t,size[t]+=size[f[k][1]];
son[t]只需通过比较size[f[k][1]]即可得出。(f[k][1]是t的一个儿子)
注意:
如果有多个最大size,任选一个;
叶节点无重儿子,非叶节点有且只有一个重儿子
标程:

void dg1(int t)
{
	size[t]=1;//点本身size=1;
	for (int k=q[t];k;k=ff[k][2]) {
		int h=ff[k][1];//ff[k][1]是前向星中t的一个儿子
		if (h!=fa[t]) {
			fa[h]=t,dep[h]=dep[t]+1;//求fa与dep
			dg1(h);
			size[t]+=size[h];//求size
			if ((!son[t])||(size[son[t]]<size[h])) son[t]=h;
			//求son,选择儿子中最大的size
		}
	}
}
//dg1跑完上图后如此:
size 14 5 2 6 1 3 1 1 3 1 1 1 2 1
son	4 6 7 9	0 11 0 0 13 0 0 0 0 0
 fa	0 1 1 1	2 2 3 4 4 4 6 6 9 13
dep	1 2 2 3 3 3 3 3	3 4 4 4	4 4

Top2:dg2
求出tree,pre与top。
为了用数据结构维护重链,我们要让重链的DFS序连续
标程:

void dg2(int t,int ad)
{
	tot++;
	tree[t]=tot,pre[tot]=t,top[t]=ad;
	//求tree与pre,tree[x]=y则pre[y]=x;
	if (!son[t]) return;
	dg2(son[t],ad);//优先递归重儿子,让重链DFS序连续
	for (int k=q[t];k;k=ff[k][2]) {
		int h=ff[k][1];//dg1中已解释
		if ((h!=fa[t])&&(h!=son[t])) dg2(h,h);
		//如果一个点位于轻链顶端,则它的链顶节点为本身
	}
}
//dg2跑完上图后如此
top	1 2 3 1 5 2 7 8	1 10 2 12 1 1
tree 1 10 8 2 14 11 9 6 3 7 12 13 4 5
pre	1 4 9 13 14 8 10 3 7 2 6 11 12 5

好了,树链剖分结束了。

WTF?这就结束了?说好150+行的代码呢?

是的,树链剖分的主要操作已经结束,我们将树转换成了一条条的链加以维护。
不过,还有一个遗留难题:为什么树链剖分的时间复杂度是O(nlog^2n)?
有两条性质:
1.若(u,v)为轻边(u是v父亲),则size[u]>size[v]*2。
这个显然,如果不满足size[u]>size[v]*2,size[v]必然不小于其它儿子的size,那么(u,v)不该是轻边。
2.从根结点到任意结点的路所经过的轻重链的个数必定都小与O(logn);
这个不显然,但易证。
先证轻链:因为1性质,如果(u,v)为轻边,从u到v的size至少减一半;
那么最多只能经过log(n)层。
重链与轻链是并行的,所以重链最多有log(n)+1条
证毕。
对于这些链,用线段树维护即可。
模板题:JZOJ2256 ZJOI2008 树的统计

#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cstring>
using namespace std;
int i,j,k,m,n,o,p,l,s,t,x,y,tot;
struct node{int sum,max,l,r;}f[1200001];
int ff[600001][3],q[600001],fa[300001],son[300001],dep[300001],size[300001];
int top[300001],tree[300001],pre[300001],a[300001];
char ch[7];
void insert(int x,int y)
{ t++,ff[t][1]=y,ff[t][2]=q[x],q[x]=t; }
void downdata(int x)
{
    int xx=x*2,yy=x*2+1;
    f[x].sum=f[xx].sum+f[yy].sum;
    f[x].max=max(f[xx].max,f[yy].max);
}
void change(int x,int v,int ad)
{
    if (f[x].l==f[x].r) f[x].max=ad,f[x].sum=ad;
    else {
        int mid=(f[x].l+f[x].r)/2;
        if (v<=mid) change(x*2,v,ad); else change(x*2+1,v,ad);
        downdata(x);
    }
}
void make(int x,int l,int r)
{
    f[x].l=l,f[x].r=r;
    if (l==r) f[x].sum=f[x].max=a[pre[l]];
    else {
        int mid=(l+r)/2;
        make(x*2,l,mid);make(x*2+1,mid+1,r);
        downdata(x);
    }
}
void dg1(int t)
{
    size[t]=1;
    for (int k=q[t];k;k=ff[k][2]) {
        int h=ff[k][1];
        if (h!=fa[t]) {
            fa[h]=t,dep[h]=dep[t]+1;
            dg1(h);
            size[t]+=size[h];
            if ((!son[t])||(size[son[t]]<size[h])) son[t]=h;
        }
    }
}
void dg2(int t,int ad)
{
    tot++;
    tree[t]=tot,pre[tot]=t,top[t]=ad;
    if (!son[t]) return;
    dg2(son[t],ad);
    for (int k=q[t];k;k=ff[k][2]) {
        int h=ff[k][1];
        if ((h!=fa[t])&&(h!=son[t])) dg2(h,h);
    }
}
int getsum(int t,int x,int y)
{
    if ((f[t].l>=x)&&(f[t].r<=y)) return f[t].sum;
    int mid=(f[t].l+f[t].r)/2,sum=0;
    if (x<=mid) sum+=getsum(t*2,x,y);
    if (y>mid) sum+=getsum(t*2+1,x,y);
    return sum;
}
int getmax(int t,int x,int y)
{
    if ((f[t].l>=x)&&(f[t].r<=y)) return f[t].max;
    int mid=(f[t].l+f[t].r)/2,Max=-2147483648;
    if (x<=mid) Max=max(Max,getmax(t*2,x,y));
    if (y>mid) Max=max(Max,getmax(t*2+1,x,y));
    return Max;
}
int findmax(int x,int y) 
{
    int xx=top[x],yy=top[y],maxans=-2147483648;
    while (xx!=yy) {
        if (dep[xx]<dep[yy]) swap(xx,yy),swap(x,y);
        maxans=max(maxans,getmax(1,tree[xx],tree[x]));
        x=fa[xx],xx=top[x];
    }   
    if (dep[x]>dep[y]) swap(x,y);
    maxans=max(maxans,getmax(1,tree[x],tree[y]));
    return maxans;
}
int findsum(int x,int y) 
{
    int xx=top[x],yy=top[y],sumans=0;
    while (xx!=yy) {
        if (dep[xx]<dep[yy]) swap(xx,yy),swap(x,y);
        sumans+=getsum(1,tree[xx],tree[x]);
        x=fa[xx],xx=top[x];
    }   
    if (dep[x]>dep[y]) swap(x,y);
    sumans+=getsum(1,tree[x],tree[y]);
    return sumans;
}
int main()
{
    scanf("%d",&n);
    for (i=1;i<=n-1;i++) {
        scanf("%d%d",&x,&y);
        insert(x,y);insert(y,x);
    }
    for (i=1;i<=n;i++) scanf("%d",&a[i]);
    t=0;dep[1]=1;
    dg1(1);
    dg2(1,1);
    make(1,1,n);
    scanf("%d",&m);
    for (i=1;i<=m;i++) {
        scanf("%s",&ch);
        scanf("%d%d",&x,&y);
        if (ch[1]=='H') {
            change(1,tree[x],y);
        } else {
            printf("%d\n",ch[1]=='M'?findmax(x,y):findsum(x,y));
        }
    }
}

今天的文章树链剖分解析—WYD分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/60071.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注