点分治

http://codeforces.com/problemset/problem/293/E
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef __int64 LL;
const int N=1e5+10;
struct Edge{
    int v,next,w;
    Edge(int v=-1,int next=-1,int w=-1):v(v),next(next),w(w){}
}e[N*2];
int head[N],total;
void init(){
    memset(head,-1,sizeof(head));total=0;
}
void adde(int u,int v,int w){
    e[total]=Edge(v,head[u],w);head[u]=total++;
}

int ss[N],sz,s[N];s[i]以i为根的节点个数,ss[i]以i为根的子树中最大节点个数,sz整棵树的节点个数
int down[N];/下推标记
int root;重心
void getroot(int u,int f){ss[] s[]
    s[u]=1;ss[u]=0;
    for(int i=head[u];i!=-1;i=e[i].next){
        int v=e[i].v;
        if(v!=f&&!down[v]){
            getroot(v,u);
            s[u]+=s[v];
            ss[u]=max(ss[u],s[v]);
        }
    }
    ss[u]=max(ss[u],sz-s[u]);
    if(ss[u]<ss[root])root=u;
}

struct PP{
    int h,w;
    PP(int h=-1,int w=-1):h(h),w(w){}
    bool operator<(const PP&th)const {
        return w<th.w;
    }
}pp[N];
int cd;

int ww[N],hh[N];/到根的距离
void getdep(int u,int f){dis[] s[]
    pp[cd++]=PP(hh[u],ww[u]);
    s[u]=1;
    for(int i=head[u];i!=-1;i=e[i].next){
        int v=e[i].v,w=e[i].w;
        if(!down[v]&&v!=f){
            hh[v]=hh[u]+1;ww[v]=ww[u]+w;
            getdep(v,u);
            s[u]+=s[v];
        }
    }
}

int n,H,W;
int cc[N];
void update(int x,int v){
    while(x<=n){
        cc[x]+=v;x+=x&-x;
    }
}
void dele(int x){
    while(x<=n)cc[x]=0,x+=x&-x;
}
int query(int x){
    int ans=0;
    while(x>0){
        ans+=cc[x];x-=x&-x;
    }
    return ans;
}

LL cal(int u,int w1,int w2){///dep[]
    cd=0;hh[u]=w1;ww[u]=w2;
    getdep(u,0);
    LL ret=0;

    sort(pp,pp+cd);
    int j=0;update(pp[0].h,1);
    for(int i=cd-1;i>=1;i--){
        if(pp[i].w+pp[0].w>W)continue;
        while(j>i-1){
            update(pp[j].h,-1);j--;
        }
        while(j+1<=i-1&&pp[i].w+pp[j+1].w<=W){
            j++;update(pp[j].h,1);
        }
        ret+=query(min(H+2-pp[i].h,n));
    }
    while(j>=0){
        update(pp[j].h,-1);j--;
    }
    return ret;
}

LL ans=0;
void work(int u){sz root ans;
    ans+=cal(u,1,0);
    down[u]=1;
    for(int i=head[u];i!=-1;i=e[i].next){
        int v=e[i].v,w=e[i].w;
        if(!down[v]){
            ans-=cal(v,2,w);
            ss[0]=sz=s[v];
            getroot(v,root=0);
            work(root);
        }
    }
}

int main(){
    #ifdef DouBi
    freopen("in.cpp","r",stdin);
    #endif // DouBi
    while(scanf("%d%d%d",&n,&H,&W)!=EOF){
        init();
        for(int i=2;i<=n;i++){
            int v,w;scanf("%d%d",&v,&w);
            adde(i,v,w);adde(v,i,w);
        }
        ans=0;
        memset(cc,0,sizeof(cc));
        memset(down,0,sizeof(down));
        ss[0]=sz=n;
        getroot(1,root=0);
        work(root);
        printf("%I64d\n",ans);
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值