poj1741 Tree,平衡树,启发式合并

楼教主男人八题之一。
点分治看腻了吧。来点新鲜的。

关于启发式合并,我的上篇博客已经写了一些,请移步 这儿


这题和hdu4812一样,也是询问 是否存在/有多少个/各种最小 满足某条件的树的路径。
树的路径问题在这里可化为树上一对点的问题。
同样,每个叶节点建一个包含自己的平衡树。
dfs回溯时,非叶节点选取含点最多的子节点的平衡树作为自己的平衡树,将其他的子节点的平衡树暴力合并到这个树上。
同时查询这个子节点中小于给定要求长度的节点的个数,更新答案即可。
注意暴力合并时,要先询问再合并,否则本树上的节点会有重复计算。

由于每个节点的插入次数小于O(logn),总插入次数不超过O(nlogn)。
用treap进行插入、查询复杂度为O(logn),因此总复杂度为O(nlognlogn)。
不过不进行删除操作的话,需要O(nlogn)的空间。
实际效果不错,157ms。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;
#define NN 10100

int tn;
struct treap{
    int l,r,fix,tot,val,cnt;
    void init(int v){
        l=r=0;
        fix=rand()*rand();
        tot=cnt=1;
        val=v;
    }
}t[NN*12];

void init_treap(){
    srand(1314);
    tn=0;
    t[0].cnt=t[0].tot=0;
    t[0].fix=-1000001000;
}


#define getot(p) \
do{\
    t[p].tot=t[p].cnt;\
    if (t[p].l) t[p].tot+=t[t[p].l].tot;\
    if (t[p].r) t[p].tot+=t[t[p].r].tot;\
}while(0);

/*
inline void getot(int p){
    t[p].tot=t[p].cnt;
    if (t[p].l) t[p].tot+=t[t[p].l].tot;
    if (t[p].r) t[p].tot+=t[t[p].r].tot;
}
*/

inline void rrot(int &p){
    int y=t[p].l;
    t[p].l=t[y].r;
    t[y].r=p;
    getot(p);
    p=y;
    getot(p);
}

inline void lrot(int &p){
     int y=t[p].r;
     t[p].r=t[y].l;
     t[y].l=p;
     getot(p);
     p=y;
     getot(p);
}
void insert(int val,int &p,int num=1){
     if (p==0) {
         p=++tn;
         t[p].init(val);
         t[p].cnt=t[p].tot=num;
         return;
     }
     t[p].tot+=num;
     if (val==t[p].val) {t[p].cnt+=num;}
     else if (val<t[p].val){
          insert(val,t[p].l,num);
          if (t[p].fix>t[t[p].l].fix) rrot(p);
     }
     else {
          insert(val,t[p].r,num);
          if (t[p].fix>t[t[p].r].fix) lrot(p);
     }
     //getot(p);
}

int queryless(int val,int p){
    if (val==t[p].val) return t[t[p].l].tot+t[p].cnt;
    else if (val<t[p].val){
        if (t[p].l) return queryless(val,t[p].l);
        else return 0;
    }
    else {
        if (t[p].r) return t[t[p].l].tot+t[p].cnt+queryless(val,t[p].r);
        else return t[t[p].l].tot+t[p].cnt;
    }
}

int fi[NN],ne[NN*2],to[NN*2],w[NN*2],te;

void addedge(int fr,int t,int val){
    ne[te]=fi[fr];fi[fr]=te;to[te]=t;w[te++]=val;
}

int n,k;

int ans;
int son[NN],tot[NN],root[NN];
int dis[NN],sw[NN];

void dfs1(int u,int fa){
    son[u]=-1;
    tot[u]=1;
    int ma=-1,e,v;
    for(e=fi[u];e!=-1;e=ne[e]){
        v=to[e];
        if (v!=fa){
            dfs1(v,u);
            if (tot[v]>ma) {ma=tot[v];son[u]=v;sw[u]=w[e];}
            tot[u]+=tot[v];
        }
    }
}

void merge(int &ru,int rv,int du,int dv){
    insert(t[rv].val+dv-du,ru,t[rv].cnt);
    if (t[rv].l) merge(ru,t[rv].l,du,dv);
    if (t[rv].r) merge(ru,t[rv].r,du,dv);
}

void query(int &ru,int rv,int du,int dv){
    int tmp,val;
    val=k-t[rv].val-du-dv;
    tmp=queryless(val,ru);
    ans+=tmp*t[rv].cnt;
    if (t[rv].l) query(ru,t[rv].l,du,dv);
    if (t[rv].r) query(ru,t[rv].r,du,dv);
}

void dfs2(int u,int fa,int dd){
    int e,v;
    if (son[u]==-1){
        root[u]=++tn;t[tn].init(0);
        dis[u]=dd;
        return;
    }
    dfs2(son[u],u,sw[u]);
    root[u]=root[son[u]];
    dis[u]=dis[son[u]];
    ans+=queryless(k-dis[u],root[u]);
    insert(-dis[u],root[u],1);
    for(e=fi[u];e!=-1;e=ne[e]){
        v=to[e];
        if (v!=fa&&v!=son[u]){
            dfs2(v,u,w[e]);
            query(root[u],root[v],dis[u],dis[v]);
            merge(root[u],root[v],dis[u],dis[v]);
        }
    }
    dis[u]+=dd;
}

int main(){
    //freopen("1741in.txt","r",stdin);
    int a,b,c,i;
    while(scanf("%d%d",&n,&k)&&(n||k)){
        te=0;
        memset(fi,-1,sizeof(fi));
        for(i=1;i<n;++i){
            scanf("%d%d%d",&a,&b,&c);
            addedge(a,b,c);
            addedge(b,a,c);
        }
        init_treap();

        dfs1(1,-1);
        ans=0;
        dfs2(1,-1,0);
        printf("%d\n",ans);
    }
    return 0;
}








  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值