关于树分块算法的一些研究

前言

树分块这种算法,本蒟蒻没有查询到太多资料。
而gty系列中,出现过如“gty的妹子树”这样的题目,流传着一种“树分块”做法。
然而,网络流传的树分块算法都可以被菊花图卡掉。
流传的方法:一个结点的父亲所在块未满,就让该结点加入其父亲所在块,否则自成一块。
至于这个方法最早的出处我就不清楚了。
本文将介绍正统的树分块算法(其实也是笔者yy的)。
先把结论放上:其实树分块并没有什么卵用。至于为什么没用,往下读呗。
可能树分块有什么特别的用处本蒟蒻不知道,有问题请留言提出!以下都是我的主观臆断。

正统分块方法

正统的分块方法应该是如“王室联邦”一题的分块方法。树块具体定义如下:
1、除根节点所在块以外,每一块内深度最小的结点的父亲相同。这个父亲被称之为该块的块顶,其中特别的根节点也是块顶。
2、每一块内非深度最小的结点的父亲一定与其处于同一块中。
3、B<=每块大小<=3B。B是你定义的一个常数(B就是决定块大小和块个数的,修改B值会影响算法的最终复杂度)
也就是说,对于块顶不在块内的结点,加上块顶这个块就联通。
如何满足这三点要求可以看代码,接着会进行解释。
这是王室联邦那道题的代码,算法执行完后便完成了分块。

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=1000+10;
int h[maxn],go[maxn*2],next[maxn*2],size[maxn],belong[maxn],cap[maxn],s[maxn];
int i,j,k,l,t,n,m,c,tot,top,cnt;
void add(int x,int y){
    go[++tot]=y;
    next[tot]=h[x];
    h[x]=tot;
}
void dfs(int x,int y){
    s[++top]=x;
    int t=h[x];
    while (t){
        if (go[t]!=y){
            dfs(go[t],x);
            size[x]+=size[go[t]];
            if (size[x]>=c){
                size[x]=0;
                cap[++cnt]=x;
                while (s[top]!=x) belong[s[top--]]=cnt;
            }
        }
        t=next[t];
    }
    size[x]++;
}
void dg(int x,int y,int z){
    if (belong[x]) z=belong[x];else belong[x]=z;
    int t=h[x];
    while (t){
        if (go[t]!=y) dg(go[t],x,z);
        t=next[t];
    }
}
int main(){
    scanf("%d%d",&n,&c);
    if (n<c){
        printf("0\n");
        return 0;
    }
    fo(i,1,n-1){
        scanf("%d%d",&j,&k);
        add(j,k);add(k,j);
    }
    dfs(1,0);
    if (!cnt) cap[cnt=1]=1;
    dg(1,0,cnt);
    printf("%d\n",cnt);
    fo(i,1,n) printf("%d%c",belong[i],(i==n)?'\n':' ');
    fo(i,1,cnt) printf("%d%c",cap[i],(i==cnt)?'\n':' ');
}

belong表示一个结点的所属块,cap表示一个块的块顶。size[x]表示以x为根的子树内,所属块块顶不在以x为根的子树内的结点有多少个。容易知道,这些结点最终会和x处于同一块。
易知这样执行算法可以满足树块定义的前两条,我们来证明为何其满足第三条。第三条定义也是能够保证算法复杂度的很重要的一条。
先看第一遍深搜,我们可以得到结论 size[x]<B ,不然就会继续分成一块。然后因为每次一达到B就会分成一块,所以第一遍深搜后每块大小至少为B至多为2B。
因为 size[x]<B ,所以第二遍深搜每块最多被塞进B个结点。
那么就满足条件了!
然后我们定义块树,块i所代表的结点的父亲设为块i的块顶所属块,那么可以建出块树。
对于通常可以使用树分块的题目,都是进行子树询问。那么因为 size[x]<B ,所以剩余部分不超过B个,直接暴力。然后接下来在块树内快速访问所有块顶都在子树内的块。
这种分块也是可以动态的,支持加点操作的话,比如随便弄个动态树链剖分就好了(大雾。

一道题目

一颗N个结点的树,逆序对(i,j)定义为i是j的祖先且ai>aj。要求在线询问一个子树内逆序对个数并且有修改一个结点的值。
这个我们可以用树分块解决!
同“Gty的文艺妹子序列”那题,ans[i,j]表示第i块内取一个元素与第j块取一个元素能形成的逆序对个数,其中i是块树中j的祖先。
sum[i,j]表示块树中以第i块为根的子树中j元素的出现次数。
num[i]表示第i块内部形成的逆序对个数。
对于询问操作,先广度搜索处理出剩余部分形成的逆序对个数,就是一层层的做再添进线段树内。然后扫描块树处理出每块内部形成的逆序对个数。接下来就只需要考虑跨块的情况。
显然对于一个块i,可以利用ans计算其影响。但第二维枚举很不值,由于可以和其造成影响的是一个子树,所以就可以dfs序变成序列上的区间,然后ans数组第二维就用线段树维护即可。
然后块与块之间就解决了。
接下来考虑剩余部分与块之间产生的影响。
因为有sum数组,只要第二维也用线段树维护即可。
然后考虑修改操作。
对ans的影响是块个数个的,对sum的影响也是块个数个的,重新计算num的复杂度是与块大小有关的。它们都不大,暴力乱搞。

树分块没卵用

像上面提到的题目甚至gty的妹子树那道题,都有比树分块优秀的做法。
考虑上面那道题。
如果只有询问操作,那我会做!可持久化线段树合并就行了,可以处理出答案数组。
有修改操作了?
对修改操作分块,每做完一块内所有询问,就暴力重构可持久化线段树。块内的询问,由于距离上一次重构所经过的修改操作次数很小,因此可以枚举并计算对答案的影响。
gty的妹子树也可以使用相同思路。
目前笔者还没有想到什么题目就树分块可搞。因为树分块只能解决子树问题,所以这种思路基本都能套上。
这里写图片描述这里写图片描述

  • 6
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
顺序查找的算法: 顺序查找是一种简单的查找方法,适用于数据元素个数较少的情况。其基本思想是从表的一端开始,顺序扫描线性表,依次将扫描到的数据元素的关键字与给定值k相比较,若相等则查找成功,否则查找失败。时间复杂度为O(n)。 折半查找的算法: 折半查找也称为二分查找,是一种针对有序数组的查找算法。其基本思想是将查找区间分成两部分,取中间位置的值与待查关键字进行比较,若相等则查找成功,否则根据中间位置的值与待查关键字的大小关系确定下一步查找的区间,直到查找成功或查找区间为空为止。时间复杂度为O(log2 n)。 分块查找的算法分块查找也称为索引顺序查找,是一种针对大量数据的查找算法。其基本思想是将数据分成若干块,每一块内部有序,块与块之间无序。同时建立一个索引表,索引表中的每个元素记录每一块中最大关键字和该块的起始位置。查找时先在索引表中查找待查关键字所在的块,再在该块内部进行顺序查找。时间复杂度为O(√n)。 二叉排序的基本运算算法的过程: 二叉排序是一种特殊的二叉,其左子上所有节点的关键字均小于根节点的关键字,右子上所有节点的关键字均大于根节点的关键字。其基本运算包括插入、删除和查找。 插入操作:从根节点开始,若待插入节点的关键字小于当前节点的关键字,则继续在当前节点的左子中查找;若待插入节点的关键字大于当前节点的关键字,则继续在当前节点的右子中查找。直到找到一个空节点,将待插入节点插入该位置。 删除操作:若待删除节点为叶子节点,则直接删除;若待删除节点只有一个子节点,则将其子节点替换待删除节点;若待删除节点有两个子节点,则找到其右子中最小的节点,将其替换待删除节点,并删除右子中最小的节点。 查找操作:从根节点开始,若待查找节点的关键字等于当前节点的关键字,则查找成功;若待查找节点的关键字小于当前节点的关键字,则继续在当前节点的左子中查找;若待查找节点的关键字大于当前节点的关键字,则继续在当前节点的右子中查找。直到找到一个空节点,查找失败。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值