点分治

点分治用来处理树上路径问题,每一次将树分治为几棵子树,然后继续递归,得到答案

每次分治时,子树的根选取为其的重心,递归的子树大小不会超过原树大小的一半,保证了时间复杂度为(O(n log n))

利用容斥原理统计答案

树上有多少对点,满足两点间的距离小于等于(k)

(code:)

void dfs_root(int x,int fa)
{
	siz[x]=1,ma[x]=0;
	for(int i=head[x];i;i=e[i].nxt)
	{
		int y=e[i].to;
		if(vis[y]||y==fa) continue;
		dfs_root(y,x);
		siz[x]+=siz[y];
		ma[x]=max(ma[x],siz[y]);
	}
	ma[x]=max(ma[x],tot-siz[x]);
	if(ma[x]<ma[root]) root=x;
}
void dfs_dis(int x,int fa)
{
	len[++len_cnt]=dis[x];
	for(int i=head[x];i;i=e[i].nxt)
	{
		int y=e[i].to,v=e[i].v;
		if(vis[y]||y==fa) continue;
		dis[y]=dis[x]+v;
		dfs_dis(y,x);
	}
}
int calc(int x,int lenth)
{
    len_cnt=0,dis[x]=lenth;
    dfs_dis(x,0);
    sort(len+1,len+len_cnt+1);
    int sum=0,l=1,r=len_cnt;
    while(l<r)
	{
	    if(len[l]+len[r]<=k) sum+=r-l,l++;
		else r--;
	}
    return sum;
}
void solve(int x)
{
    vis[x]=true,ans+=calc(x,0);
    for(int i=head[x];i;i=e[i].nxt)
    {
        int y=e[i].to,v=e[i].v;
        if(vis[y]) continue;
        ans-=calc(y,v);
        root=0,tot=siz[y];
        dfs_root(y,0);
        solve(root);
    }
}

......

tot=ma[0]=n;
dfs_root(1,0);
solve(root);

动态点分治

需要支持修改时,考虑用动态点分治解决

通过构建一棵点分树,然后在点分树上修改,通过不断向上来跳父亲来维护信息

点分树为点分治时每一层的重心之间连边的一棵树,高度为(log n)

捉迷藏:给定一棵树,树上的点为黑色或白色,初始都为黑色,支持修改颜色和询问两个距离最远的黑点的距离

维护三个堆:

(q1):维护全局答案((q2)中最大值和次大值之和)

(q2):维护一个节点到在点分树上所有儿子的子树内所有黑点的最大距离(节点所有儿子(q3)的堆顶)

(q3):维护一个节点到在点分树中其子树内所有黑点到该节点父亲的距离

(q2)的存在防止了最大值和次大值的合并时,两条链来自同一子树

(code:)

void dfs_root(int x,int fa)
{
    size[x]=1,ma[x]=0,s[++cnt]=x;
    for(int i=head[x];i;i=e[i].nxt)
    {
        int y=e[i].to;
        if(vis[y]||y==fa) continue;
        dfs_root(y,x);
        size[x]+=size[y];
        ma[x]=max(ma[x],size[y]);
    }
    ma[x]=max(ma[x],tot-size[x]);
    if(ma[x]<ma[root]) root=x;
}
void solve(int x)
{
    vis[x]=true;
    q2[x].push(0);
    for(int i=1;i<=cnt;++i)
        q3[x].push(dis(fa[x],s[i]));
    for(int i=head[x];i;i=e[i].nxt)
    {
        int y=e[i].to;
        if(vis[y]) continue;
        root=cnt=0,tot=size[y];
        dfs_root(y,x);
        fa[root]=x,y=root;
        solve(root);
        q2[x].push(q3[y].top());
    }
    q1.push(q2[x].len());
}
void modify(int x,int type)
{
    q1.del(q2[x].len());
    if(type) q2[x].del(0);
    else q2[x].push(0);
    q1.push(q2[x].len());
    for(int i=x;fa[i];i=fa[i])
    {
        int l1=q3[i].top(),l2,dist=dis(fa[i],x);
        if(type) q3[i].del(dist);
        else q3[i].push(dist);
        l2=q3[i].top();
        q1.del(q2[fa[i]].len());
        if(l1!=-inf) q2[fa[i]].del(l1);
        if(l2!=-inf) q2[fa[i]].push(l2);
        q1.push(q2[fa[i]].len());
    }
}

点分树树高为 (log n),所有点的子树和为 (n log n)

原文地址:https://www.cnblogs.com/lhm-/p/12229531.html