bzoj1468: Tree

本文介绍了一道关于点分治的算法题目,通过寻找树的重心并构建子树来解决问题。详细阐述了算法流程,包括如何计算距离和、查找重心及实现点分治的过程,并附带完整的代码实现。

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

链接

  http://www.lydsy.com/JudgeOnline/problem.php?id=1468

题解

  点分治裸题。
  说下过程,就是你每次找重心然后以重心为根进行建树,算出所有点到根的 dist ,扔到一个表里,两个指针扫一遍得出结果。然后分治下去,在找重心之前应该先 dfs 一遍把当前子树里的 dist 扔进表里,扫一遍把结果在 ans 中减去。
  重心就是到所有点的距离之和最小的点。

代码

//点分治 
#include <cstdio>
#include <algorithm>
#define maxn 40010
#define forp for(int p=head[pos];p;p=nex[p])if(to[p]^pre and !grey[to[p]])
using namespace std;
int N, K, head[maxn], to[maxn<<1], nex[maxn<<1], w[maxn<<1], dist[maxn], G, list[maxn],
    tot, size[maxn], sumG, ans;
bool grey[maxn];
inline void adde(int a, int b, int v)
{to[++tot]=b;w[tot]=v;nex[tot]=head[a];head[a]=tot;}
inline int calc()
{
    int cnt=0, l=1, r=*list;
    sort(list+1,list+*list+1);
    while(l<r)
    {
        while(l<r and list[l]+list[r]>K)r--;
        while(l<r and list[l]+list[r]<=K)cnt+=r-l, l++;
    }
    return cnt;
}
void dfs_jian(int pos, int pre)
{
    list[++*list]=dist[pos];
    for(int p=head[pos];p;p=nex[p])if(to[p]^pre and !grey[to[p]])dfs_jian(to[p],pos);
}
void calc_dist(int pos, int pre)
{
    list[++*list]=dist[pos];
    forp dist[to[p]]=dist[pos]+w[p], calc_dist(to[p],pos);
}
int calc_size(int pos, int pre)
{
    size[pos]=1;
    forp size[pos]+=calc_size(to[p],pos);
    return size[pos];
}
void findG(int pos, int pre, int sum)
{
    if(sum<sumG)G=pos, sumG=sum;
    int x=1;
    forp x+=size[to[p]];
    forp findG(to[p],pos,sum+(x-size[to[p]])*w[p]-size[to[p]]*w[p]);
}
void solve(int pos)
{
    int x, i;
    if(G)*list=0, dfs_jian(pos,-1), ans-=calc();
    *list=0, calc_dist(pos,-1);
    calc_size(pos,-1);
    for(i=1,x=0;i<=*list;i++)x+=list[i];
    G=pos, findG(pos,-1,sumG=x);
    grey[G]=1, dist[G]=0;
    *list=0, calc_dist(G,-1);
    ans+=calc();
    for(int p=head[G];p;p=nex[p])if(!grey[to[p]])solve(to[p]);
}
void init()
{
    int a, b, v, i;
    scanf("%d",&N);
    for(i=1;i<N;i++)scanf("%d%d%d",&a,&b,&v), adde(a,b,v), adde(b,a,v);
    scanf("%d",&K);
}
int main()
{
    init();
    solve(1);
    printf("%d",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值