初学点分治

初学点分治(静态)


引入

  • 我们有时候会遇到这样一些题目:

    给定一颗树(一般是无根的),然后多次询问你任意两点之间的信息。如果我们直接暴力,时间复杂度很明显是O(n*m)的。

m次的查询很明显是无法优化的,那么我们考虑优化一下n。是否能做到预处理后O(logn*m)或者直接O(m*1)的时间复杂度呢?或者换个说法,我们能不能提前把两点之间的信息先处理出来以供查询?

现在我们提供一种思想,就是把树拆开。
怎么拆?


原理及实现

树的分治,就是把树从一个完整的树分成大大小小的子树来统计。

比如

我们先处理好以A为根节点的点对
即每一个点到点A的距离,那么以B,E为例,它们之间的距离就是BA到EA的距离之和。
然后去掉A,剩下B和E为根的树,重复以上步骤,直到所有点都作为根被遍历过。

实现步骤如下

  1. 寻找树的重心

因为这是一颗无根树,所以我们考虑一下选定一个根。
而树的dfs一般都是按深度搜下去的,我们自然希望深度最小以保证时间复杂度。
举个例子:
给你一条链,你是每次从链首一个一个拆开好还是从中间往两边扩展好?
答案很明显是从中间好,因为链首的复杂度是O(n)的,链中间是O(logn)的。

那么我们要怎么寻找呢?

我们假定一个点,搜出它的所有子树的大小,并从中找到最大的一个,与总点数减去这个点的子树和相比较、
为什么?因为树的重心定义如下:

找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心,删去重心后,生成的多棵树尽可能平衡。
void getroot(int x,int fa) 
{ 
    son[x]=1; 
    int ret=0; 
    for(int i=head[x];i;i=e[i].next) 
    { 
        int to=e[i].to; 
        if(!vis[to]&&to!=fa) 
        { 
            getroot(to,x); 
            son[x]+=son[to]; 
            ret=max(ret,son[to]); 
        }
    } 
    ret=max(ret,size-son[x]); if(ret<minn)minn=ret,root=x; 
}
  1. 根据容斥原理处理出点之间的信息

上面已经提到,我们处理点之间的信息,是处理这个点到根节点的信息。
再以这张图为例

因为是直接递归,而树并不知道那个点是它的左子树,哪个是右子树,
所以会有一下路径别处理出来:
A—>A
A—>B
A—>B—>C
A—>B—>D
A—>E
A—>E—>F
那么我们在合并答案是会将上述6条路径两两进行合并。
但是按A—>B—>C 和 A—>B—>D
我又要求B->C的距离,那信息岂不是A->B->C+A->B,这显然是不行的
减去重边,就是减去每个子树的单独贡献。
例如对于以B为根的子树,就会减去:
B—>B
B—>C
B—>D
这个要结合代码来体现。
(注:此代码来至洛谷点分治模版)


void dep(int x,int fa,int v) //求出当前点为根时,它的子树信息
{ 
    vi[++tot]=v; 
    for(int i=head[x];i;i=e[i].next) 
    { 
        int to=e[i].to; 
        if(to!=fa&&!vis[to]) 
        dep(to,x,v+e[i].v); 
    } 
}

void cal(int x,int f,int v) //根据容斥原理计算子树信息
{ 
    tot=0; dep(x,x,0); 
    for(int i=1;i<=tot;i++) 
        for(int j=1;j<=tot;j++) //一般这个统计格式适用于大多数题目。
        if(f&&vi[i]+vi[j]<=k)
        num[vi[i]+vi[j]]++; 
        else  if(vi[i]+vi[j]+v<=k)
        num[vi[i]+vi[j]+v]--; 
}   

void dfs(int x) 
{ 
    cal(x,1,0); vis[x]=1; //计算每一个点到根的贡献
    for(int i=head[x];i;i=e[i].next) 
    { 
    int to=e[i].to; 
    if(vis[to])continue; 
    cal(to,0,e[i].v*2); //减去重边,注意,这里的边值是被计算了两次的,要*2
    minn=n,size=son[to];
    getroot(to,x); dfs(root); 
    } 
}

好像已经没什么好讲的了。

猜你喜欢

转载自www.cnblogs.com/hhh1109/p/9187851.html
今日推荐