poj1741 Tree【点分治模板】

本文介绍了一种基于点分治的算法,用于解决特定类型的树形结构问题。具体而言,该算法可以高效地计算出一棵边带权树中距离不超过给定阈值的所有点对数目。通过将树进行分解,并利用排序和点对计数技巧,实现了O(nlog²n)的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

突然发现自己没写点分治的板子,所以来填填坑。

题目大意:

给定一棵边带权树,为树中距离不超过kk的点对数目。

解题思路:

先点分,考虑所有经过u的链。
求出当前子树中所有点与u的距离。
排序后O(n)求出和小于kk的点对数。
减掉属于u的同一个孩子的点对数。
复杂度O(nlog2n)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

int getint()
{
    int i=0,f=1;char c;
    for(c=getchar();(c<'0'||c>'9')&&c!='-';c=getchar());
    if(c=='-')f=-1,c=getchar();
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}

const int N=10005;
int n,k,ans,root,totsize,cnt;
int tot,first[N],nxt[N<<1],to[N<<1],w[N<<1];
int dis[N],maxsub[N],size[N],tmp[N];
bool vis[N];

void add(int x,int y,int z)
{
    nxt[++tot]=first[x],first[x]=tot,to[tot]=y,w[tot]=z;
}

void findroot(int u,int fa)
{
    size[u]=1,maxsub[u]=0;
    for(int e=first[u];e;e=nxt[e])
    {
        int v=to[e];
        if(v==fa||vis[v])continue;
        findroot(v,u);
        size[u]+=size[v];
        maxsub[u]=max(maxsub[u],size[v]);
    }
    maxsub[u]=max(maxsub[u],totsize-size[u]);
    if(maxsub[u]<maxsub[root])root=u;
}

void getdis(int u,int fa)
{
    tmp[++cnt]=dis[u];
    for(int e=first[u];e;e=nxt[e])
    {
        int v=to[e];
        if(v==fa||vis[v])continue;
        dis[v]=dis[u]+w[e];
        getdis(v,u);
    }
}

int calc(int u,int init)
{
    cnt=0,dis[u]=init;
    getdis(u,0);sort(tmp+1,tmp+cnt+1);
    int res=0;
    for(int l=1,r=cnt;l<r;)
        tmp[l]+tmp[r]<=k?res+=r-l,l++:r--;
    return res;
}

void work(int u)
{
    vis[u]=1,ans+=calc(u,0);
    for(int e=first[u];e;e=nxt[e])
    {
        int v=to[e];
        if(vis[v])continue;
        ans-=calc(v,w[e]);
        root=0,maxsub[0]=totsize=size[v];
        findroot(v,u),work(root);
    }
}

int main()
{
    //freopen("lx.in","r",stdin);
    int x,y,z;
    while(n=getint())
    {
        if(!n)break;
        k=getint();
        tot=ans=0;
        memset(first,0,sizeof(first));
        memset(vis,0,sizeof(vis));
        for(int i=1;i<n;i++)
        {
            x=getint(),y=getint(),z=getint();
            add(x,y,z),add(y,x,z);
        }
        root=0,maxsub[0]=totsize=n;
        findroot(1,0),work(root);
        printf("%d\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值