POJ1741点分治

点分治入门题……
因为对于一个点,只有两种情况,路径经过当前点OR不经过……
第二种情况等价于递归求解……
设dep[i]为第i个点到当前重心的深度,
对于经过的情况,即不在一颗子树(或只在一颗)的合法路径,即对于满足dep[i]+dep[j]<=K的i、j的总方案数,减去满足路径有重合(如下图有色路径)的方案数。
这里写图片描述

#include <cstdio>
#include <cstring>
#include <algorithm>
#define INF 99999999999
#define maxn 10000+10
#define For(x) for (int k=head[x];k;k=b[k].next)
using namespace std;
struct xx{
    int v,next,q;
}b[maxn<<1];
int vis[maxn],head[maxn],//vis是否work完i点 
    f[maxn],//i点删掉后最多点数的联通块的点数 
    sz[maxn],//tree.size
    d[maxn],//i点到重心的深度 
    dep[maxn],//存在当前子树的d[] 
    sum,ans,rot,n,m,K,x,y,z;

void add(int u,int v,int q)
{
    b[++m]=(xx){v,head[u],q};
    head[u]=m;
}

void getroot(int x,int fa)//找重心 
{
    sz[x]=1;f[x]=0;
    For(x)
    {
        int v=b[k].v;
        if (v==fa||vis[v])continue;
        getroot(v,x);
        sz[x]+=sz[v];
        f[x]=max(f[x],sz[v]);
    }
    f[x]=max(f[x],sum-sz[x]);//找删掉x点后最多点数联通块的点数
    rot=f[x]<f[rot]?x:rot;
}

void getdeep(int x,int fa)//找出当前子树所有点到重心的距离 
{
    dep[++dep[0]]=d[x];
    For(x)
    {
        int v=b[k].v;
        if (v==fa||vis[v])continue;
        d[v]=d[x]+b[k].q;
        getdeep(v,x);
    }
}

int cal(int x,int now)//now为当前子树根到父亲的路径权值 
{
    d[x]=now;dep[0]=0;
    getdeep(x,0);
    sort(dep+1,dep+1+dep[0]);
    int t=0,l,r;
    for (l=1,r=dep[0];l<r;)
      dep[l]+dep[r]<=K?t+=r-l,l++:r--;
    return t;
}

void work(int x)//分治 
{
    vis[x]=1;
    ans+=cal(x,0);
    For(x)
    {
        int v=b[k].v;
        if (vis[v])continue;
        ans-=cal(v,b[k].q);//减去重复 
        sum=sz[v];
        rot=0;
        getroot(v,x);
        work(rot);
    }
}

int main()
{
    while (1)
    {
        scanf("%d%d",&n,&K);
        memset(vis,0,sizeof(vis));//初始化 
        memset(head,0,sizeof(head));
        m=ans=rot=0;
        if (n==0&&K==0) break;
        for (int i=1;i<n;i++)scanf("%d%d%d",&x,&y,&z),add(x,y,z),add(y,x,z);
        sum=n;f[0]=INF;
        getroot(1,0);
        work(rot);
        printf("%d\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值