树的点分治

37 篇文章 3 订阅
5 篇文章 0 订阅

用途

统计所有树上两点的路径的信息,常规是 O(n2) ,用此算法可已达到 O(R×nlog2n) (R为维护的数据结构时间复杂度)。

主要思路

通过寻找树的重心,把树分成若干块(重心的子树),再分治处理每个子树。
(重心是一个结点使它的子树中结点个数最大的值最小)

由于重心的单个子树的结点个数一定小于整个树结点树的一半,所以每次使用重心分割,都能将这个树规模至少降低一半。

实现

重心

siz[i] 表示结点i为根的子树的结点数;
val[i]表示这个结点的儿子为根的子树的结点数最大值(重心即为val值最小的结点);

int val[MAXN],siz[MAXN],stk[MAXN],top;
void calc_g(int u,int pa)//计算val值和siz值
{
    stk[++top]=u;//将处理过的结点放入栈
    siz[u]=1;val[u]=0;
    for(Edge *p=V[u];p;p=p->nxt)
    {
        int v=p->v;
        if(v!=pa&&)
        {
            calc_g(v,u);
            siz[u]+=siz[v];
            val[u]=max(val[u],siz[v]);
        }
    }
}
int get_center(int u)//获取重心
{
    top=0;
    calc_g(u,0);
    int res,best=0x7FFFFFFF;
    for(int i=1;i<=top;i++)
    {
        int v=stk[i];
        val[v]=max(val[v],siz[u]-siz[v]);//结点v连父亲的边的结点数也要考虑
        if(best>val[v])
        {best=val[v];res=v;}
    }
    return res;//重心
}
分治

找一个重心,然后把重心的邻接点放入栈(表示被分出来的其它区块),然后处理每个区块,维护需要的数据结构,用vis标记此时的重心,这样下次就不会访问这个重心,从而下次的区块就被限制了,无法通过重心到其它区块。

一个例子:

//SPOJ_FTOUR2的部分代码
int solve()
{
    head=tail=1;
    que[1]=1;
    int res=0;
    while(head<=tail)
    {
        int u=que[head++];
        int g=find_center(u);//找到此区块的重心
        BIT::insert(crow[g],0,id);//本题维护了一个树状数组,O(logn)
        vis[g]=true;//标记重心
        for(Edge *p=V[g];p;p=p->nxt)
        {
            int v=p->v;
            if(!vis[v])
            {
                top=0;
                res=max(res,calc(v,g,crow[v],p->val));//遍历区块,维护数据结构
                for(int i=1;i<=top;i++)
                    BIT::insert(stk[i].first+crow[g],stk[i].second,id);//继续维护数据结构
                que[++tail]=v;//将新的区块入队
            }
        }
    }
    return res;
}

例题一道

POJ2114
题意

给一棵树,边带权,求这棵树中是否有长度恰好为K的路径。

树的点分治

树
u为找到的重心,然后访问每个区块,把与u的距离放进集合S。
一边放进集合S,一边检查集合中是否已经存在路径,使得与当前距离相加刚好等于K

如果没有,继续分治:
区块
OK!

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值