POJ 1741 Tree 点分治

11 篇文章 0 订阅

POJ 1741

一道点分治模板题 树上距离小于等于k 实际上由于是最短距离 我们在统计上级子树的时候容易把下面子树内的情况考虑进去 导致多选 所以要给下面子树加爸爸边的贡献 删除这些影响 这点也是点分治的精华所在

/*
POJ 1741
*/
#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
#include <iostream>
#include <stack>
#include <set>
#include <map>
#include <sstream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAX_N = 10024;
int f[MAX_N],siz[MAX_N];
struct edge{
    int next,v,dis;
}e[MAX_N<<1];
int eid,p[MAX_N],use[MAX_N],Siz,rt,cnt,d[MAX_N],k,ans;
inline int read()
{
    int date = 0,m = 1;
    char ch = 0;
    while(ch!='-'&&(ch<'0'||ch>'9'))
        ch = getchar();
    if(ch=='-')
    {
        m = -1;
        ch = getchar();
    }
    while(ch>='0' && ch<='9')
    {
        date = date*10+ch-'0';
        ch = getchar();
    }
    return date*m;
}
inline void write(ll qw)
{
    if(qw<0)
    {
        putchar('-');
        qw = -qw;
    }
    if(qw>9)
        write(qw/10);
    putchar(qw%10+'0');
}
void init(){
    memset(p,-1,sizeof(p));
    memset(use,0,sizeof(use));
    memset(d,0,sizeof(d));
    rt = 0;
    eid = 0;
}
void Insert(int u,int v,int dis){
    e[eid].v = v;
    e[eid].dis = dis;
    e[eid].next = p[u];
    p[u] = eid++;
}
void get_rt(int u,int fa){//u为当前点,fa为父亲节点
    f[u] = 0, siz[u] = 1;//f表示这个点最大子树的大小,siz是这个点子树大小的和
    for(int i = p[u];i!=-1;i = e[i].next){//枚举儿子
        int y = e[i].v;
        if(use[y]||y==fa) continue;//use表示之前遍历过了,这里没啥用
        get_rt(y,u);//往下遍历
        f[u] = max(f[u],siz[y]);//更新f
        siz[u] += siz[y];
    }
    f[u] = max(f[u],Siz - siz[u]);//Siz表示在现在这棵子树中点的总数,开始时Siz=n,除了枚举的儿子所在的子树外,还有一棵子树是上面的那一堆,容斥原理
    if(f[u]<f[rt]) rt = u;//更新root
}

int look1(int l,int x){
    int ans = 0,r = cnt;
    while(l<=r){
        int mid(l+r>>1);
        if(d[mid]<x) l = mid + 1;
        else ans = mid, r = mid - 1;
    }
    return ans;
}

int look2(int l,int x){
    int ans = 0,r = cnt;
    while(l<=r){
        int mid(l+r>>1);
        if(d[mid]<=x) ans = mid,l = mid + 1;
        else r = mid - 1;
    }
    return ans;
}

void get_num(int u,int fa,int D){
    for(int i = p[u];i!=-1;i=e[i].next){
        int y = e[i].v;
        if(use[y]||y==fa) continue;
        d[++cnt] = D + e[i].dis;
        get_num(y,u,d[cnt]);
    }
}

int get_ans(int u,int D){//solve
    d[cnt=1] = D;
    get_num(u,0,D);
    sort(d+1,d+1+cnt);
    /*int l = 1,ans = 0;
    while(l<cnt&&d[l]+d[cnt]<k) ++l;
    while(l<cnt&&k-d[l]>=d[l]){
        int D1 = look1(l+1,k-d[l]);
        int D2 = look2(l+1,k-d[l]);
        if(D2>=D1) ans+=D2-D1+1;
        ++l;
    }
    return ans;*///等于k

    int l = 1,r = cnt,tep = 0;
    while(l<=r){
        if(d[r]+d[l]<=k) {tep = tep + r - l;l++;}
        else r--;
    }
    return tep;//小于等于k

}

void dfs(int u){//Divide
    use[u] = 1,ans+=get_ans(u,0);
    for(int i = p[u];i!=-1;i=e[i].next){
        int y = e[i].v;
        if(use[y]) continue;
        ans-=get_ans(y,e[i].dis);
        Siz = siz[y],rt = 0;
        get_rt(y,u),dfs(rt);
    }
    return ;
}
int main(){
    int n;
    while(scanf("%d%d",&n,&k)==2){
        if(n==0) break;
        cnt = 1;
        init();
        for(int i = 1;i<n;++i){
            int a,b,dis;
            a = read(),b = read(),dis = read();
            Insert(a,b,dis);
            Insert(b,a,dis);
        }
        ans = 0;
        f[rt] = Siz = n;
        get_rt(1,0);
        dfs(1);
        printf("%d\n",ans);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值