点分治

点分治&动态点分治

Tags:数据结构

作业部落

评论地址


点分治

一、板子

鉴于没有人会看我的博客学习点分治,所以就不详细解说算法了,提供一个板子逼着自己背下来
板子:[luogu3806]【模板】点分治1

求树上是否存在距离为\(K\)的路径,多组询问

点分治支持以下函数
Getroot Getdis Calc solve

1.找重心

void Getroot(int x,int fa)
{
    siz[x]=1;mx[x]=0;
    for(int i=head[x];~i;i=a[i].next)
    {
        int R=a[i].to;
        if(R==fa||vis[R]) continue;
        Getroot(R,x);
        siz[x]+=siz[R];
        mx[x]=max(mx[x],siz[R]);
    }
    mx[x]=max(mx[x],sum-siz[x]);
    if(mx[x]<mx[root]) root=x;
}

2.把树上距离存进临时数组

void Getdis(int x,int fa)
{
    dis[++cnt]=dep[x];
    for(int i=head[x];~i;i=a[i].next)
    {
        int R=a[i].to;
        if(vis[R]||R==fa) continue;
        dep[R]=dep[x]+a[i].w;
        Getdis(R,x);
    }
}

3.点分治不同题的区别就在于Calc函数

void Calc(int x,int d,int op)
{
    cnt=0;
    dep[x]=d;
    Getdis(x,0);
    sort(dis+1,dis+cnt+1);
    for(int i=1;i<=M;i++)
    {
        int l=1,r=cnt;
        while(l<r)
            if(dis[l]+dis[r]>K[i]) r--;
            else
            {
                if(dis[l]+dis[r]==K[i])
                    for(int k=r;dis[k]==dis[r]&&k>=l;k--)
                        Yes[i]+=op;
                l++;
            }
    }
}

4.递归计算,分层找重心

void Solve(int x)
{
    Calc(x,0,1);
    vis[x]=1;int tt=sum;
    for(int i=head[x];~i;i=a[i].next)
    {
        int R=a[i].to;
        if(vis[R]) continue;
        Calc(R,a[i].w,-1);
        sum=siz[R]>siz[x]?tt-siz[x]:siz[R];//siz要注意了————Cwen
        root=0;
        Getroot(R,x);Solve(root);
    }
}

5.\(main\)函数中的调用

int main()
{
    root=0;mx[0]=sum=N;
    Getroot(1,0);Solve(root);
}

注意有些题不能容斥

二、用处

在我所做过的题中,他可以用来求解树上路径问题
1.长为\(k\)(小于\(k\))的路径条数(以及这条路径的最少边数)
2.长为三的倍数的路径条数
3.路径点权之积\(\%p=k\)的字典序最小点对

动态点分治

区别在于递归时要记录上级重心,由此产生的父子关系构成点分树
于是在点分树上维护很多很多东西就可以支持动态修改了
大家看看这题-幻想乡战略游戏(动态维护带权重心)

题单

点分治

动态点分治

posted @ 2018-08-15 15:04  饕餮传奇  阅读(392)  评论(2编辑  收藏  举报