点分治总结

点分治


概念

树的点分治是一种解决一类树上路径问题的算法,复杂度大概是O(NlgN)的.
主要操作有findroot cal solve函数,根据题意还可以加其它的函数
主要是每次利用树的重心最大size的子树不超过n/2的性质达到lg的时间复杂度
然后在子树之间统计


模板

1.findroot

这个应该比较简单,找树的重心,维护点size然后求max,最后n-size[x]代表它父亲那块的子树的size

void getroot(int poi,int lst)
{
    son[poi]=1;f[poi]=0; 
    for(int i=last[poi];i;i=e[i].lst)
        if(e[i].to!=lst && !vis[e[i].to])
        {
            getroot(e[i].to,poi);
            son[poi]+=son[e[i].to];
            f[poi]=max(f[poi],son[e[i].to]);
        }
    f[poi]=max(f[poi],n-son[poi]);
    if(f[root]>f[poi])root=poi;
} 

2.cal

基本上就是dfs一路统计 以bzoj 2599为例,cal求权值和为k的最小边数,t[i]代表权值和为i的最小边数

void cal(int poi,int lst)
{
    if(dis[poi]<=m)ans=min(ans,t[m-dis[poi]]+d[poi]);
    for(int i=last[poi];i;i=e[i].lst)
        if(e[i].to!=lst && !vis[e[i].to])
        {
            d[e[i].to]=d[poi]+1;
            dis[e[i].to]=dis[poi]+e[i].c;
            cal(e[i].to,poi);
        }
}

3.solve
每次询问一个子树,再把它的信息计入到一个集合里,每次子树与其他子树的询问在这个集合里面(避免重复)
然后对于每颗子树再findroot再solve一下
以bzoj 2599为例.

void work(int poi)
{
    vis[poi]=1;t[0]=0;
    for(int i=last[poi];i;i=e[i].lst)
        if(!vis[e[i].to])
        {
            d[e[i].to]=1,dis[e[i].to]=e[i].c;
            cal(e[i].to,0);
            add(e[i].to,0,0);
        }
                                            计数部分

----------


    for(int i=last[poi];i;i=e[i].lst)
        if(!vis[e[i].to])
            add(e[i].to,0,1);
                                            这里是把处理数组清空

----------


    for(int i=last[poi];i;i=e[i].lst)
        if(!vis[e[i].to])
        {
            root=0;size=son[e[i].to];
            findroot(e[i].to,poi);
            work(root);
        }
                                            重新找根和更新size
}

大体操作就这些 个人感觉比较容易

你可能感兴趣的:(点分治)