点分治模板 (例题:树中点对距离) 待更新【坑】

6 篇文章 0 订阅
2 篇文章 0 订阅

待更新

Description
给出一棵带边权的树,问有多少对点的距离<=Len

c表示最大儿子的节点的size
size表示子树大小

处理子树大小

void Get_Size(int x,int y)
{
    size[x]=1;
    c[x]=0;
    for (int k=last[x];k;k=next[k])
    {
        if (t[k]==y || bz[t[k]]) continue;
        Get_Size(t[k],x);
        size[x]+=size[t[k]];
        c[x]=max(c[x],size[t[k]]);
    }   
}

找重心
//size[r]-size[x]是x上面部分的树的尺寸,跟x的最大孩子比,找到最大孩子的最小差值节点

void Get_Root(int r,int x,int y)
{
    c[x]=max(c[x],size[r]-size[x]);
    if (c[x]<Max) Max=c[x],root=x;
    for (int k=last[x];k;k=next[k])
    {
        if (t[k]==y || bz[t[k]]) continue;
        Get_Root(r,t[k],x);
    }
}

求每个点离重心的距离

void Get_Dis(int x,int sum,int y)
{
    dis[++num]=sum;
    for (int k=last[x];k;k=next[k])
    {
        if (t[k]!=y && !bz[t[k]])
            Get_Dis(t[k],sum+val[k],x);
    }
}

计算以x为根的子树中有多少点对的距离小于等于len

int Calc(int x,int sum)
{
    int Res=0;
    num=0;
    Get_Dis(x,sum,0);
    sort(dis+1,dis+num+1);
    int i=0,j=num;
    while (i<j)
    {
        i++;
        while (dis[i]+dis[j]>len && i<j) j--;
        Res+=j-i;
    }
    return Res;
}

大肥水

void Dfs(int x)
{
    Max=n;
    Get_Size(x,0);
    Get_Root(x,x,0);
    ans+=Calc(root,0);
    bz[root]=true;
    for (int k=last[root];k;k=next[k])
    {
        if (!bz[t[k]])
        {
            ans-=Calc(t[k],val[k]);
            Dfs(t[k]);
        }
    }
}

CODE

#include <cstdio>
#include <iostream>
#include <algorithm>
#define fo(i,a,b) for (int i=a;i<=b;i++)
#define N 10005

using namespace std;

int n,len,l=0,ans=0,Max=0,root=0;
int t[N*2],last[N],next[N*2],val[N*2],size[N],c[N],dis[N],num=0;
bool bz[N];

void add(int x,int y,int z)
{
    t[++l]=y;
    next[l]=last[x];
    last[x]=l;
    val[l]=z;
}

void Get_Size(int x,int y)
{
    size[x]=1;
    c[x]=0;
    for (int k=last[x];k;k=next[k])
    {
        if (t[k]==y || bz[t[k]]) continue;
        Get_Size(t[k],x);
        size[x]+=size[t[k]];
        c[x]=max(c[x],size[t[k]]);
    }   
}

void Get_Root(int r,int x,int y)
{
    c[x]=max(c[x],size[r]-size[x]);
    if (c[x]<Max) Max=c[x],root=x;
    for (int k=last[x];k;k=next[k])
    {
        if (t[k]==y || bz[t[k]]) continue;
        Get_Root(r,t[k],x);
    }
}

void Get_Dis(int x,int sum,int y)
{
    dis[++num]=sum;
    for (int k=last[x];k;k=next[k])
    {
        if (t[k]!=y && !bz[t[k]])
            Get_Dis(t[k],sum+val[k],x);
    }
}

int Calc(int x,int sum)
{
    int Res=0;
    num=0;
    Get_Dis(x,sum,0);
    sort(dis+1,dis+num+1);
    int i=0,j=num;
    while (i<j)
    {
        i++;
        while (dis[i]+dis[j]>len && i<j) j--;
        Res+=j-i;
    }
    return Res;
}

void Dfs(int x)
{
    Max=n;
    Get_Size(x,0);
    Get_Root(x,x,0);
    ans+=Calc(root,0);
    bz[root]=true;
    for (int k=last[root];k;k=next[k])
    {
        if (!bz[t[k]])
        {
            ans-=Calc(t[k],val[k]);
            Dfs(t[k]);
        }
    }
}

int main()
{
    scanf("%d%d",&n,&len);
    fo(i,1,n-1)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);
        add(y,x,z);
    }
    Dfs(1);
    printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值