「CF293E」Close Vertices【点分治+主席树+双指针】

题目链接

题意

  • 给你一颗带有边权的树,求有多少点对 ( u , v ) (u,v) (u,v)满足从 u u u v v v的路径上最多有 l l l条边,总边权最多为 w w w

题解

  • 对于经过重心的点对,由于边数大不一定总权值也打,所以两者不同步,考虑先根据边数排序,然后对权值和建一颗主席树,双指针扫一下找到醉的位置满足边总数的要求,然后主席树查询区间小于 w − d i s [ i ] . a w-dis[i].a wdis[i].a的数量,其中 d i s [ i ] . a dis[i].a dis[i].a表示当前枚举的权值和

复杂度

  • O ( n ( log ⁡ n ) 2 ) O(n(\log n)^2) O(n(logn)2)

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
const int maxm=4e6+10;
int n,k,l;
namespace president_tree{
    int sum[maxm], cnt, ls[maxm], rs[maxm], roo[maxn], val[maxn], tot;  
    inline void init() {cnt=0;}    
    inline int build(int l, int r)   {
        int now = ++cnt;
        if(l == r) return now;
        int mid = (l + r) >> 1;
        ls[now] = build(l, mid);
        rs[now] = build(mid + 1, r);
        return now;
    }
    inline int modify(int l, int r, int loc, int pre) {                                          
        int now=++cnt;
        sum[now]=sum[pre]+1;
        if(l==r) return now;
        int mid=(l+r)>>1;
        if(loc<=mid){
            rs[now]=rs[pre];
            ls[now]=modify(l, mid, loc, ls[pre]);
        }else{
            ls[now]=ls[pre];
            rs[now]=modify(mid+1,r,loc,rs[pre]);
        }
        return now;
    }
    inline void build_tree(int a[],int n) {
        init();
        for(int i=1;i<=n;i++) val[i]=a[i];
        sort(val+1,val+n+1);
        tot=unique(val+1,val+n+1)-val-1;
        roo[0]=build(1,tot);
        for(int i=1;i<=n;i++) {
            int loc=lower_bound(val+1,val+tot+1,a[i])-val;
            roo[i]=modify(1,tot,loc,roo[i-1]);
        }
    }
    inline int query(int l, int r, int k, int a, int b) {
        if(l==r) return val[l]<=k?sum[b]-sum[a]:0;
        if(l>r) return 0;
        int mid=(l+r)>>1;
        if(k>val[mid]) return sum[ls[b]]-sum[ls[a]]+query(mid+1,r,k,rs[a],rs[b]);
        return query(l,mid,k,ls[a],ls[b]);
    }
}using namespace president_tree;

namespace point_divide_and_conquer{
    int tot,head[maxn],siz[maxn],c[maxn],root,min_son,num;
    bool vis[maxn];
    struct data {
        int a,b;
        data(int c=0,int d=0) {a=c,b=d;}
        friend bool operator<(const data &u,const data &v) {
            if(u.b==v.b) return u.a<v.a;
            return u.b<v.b;
        }
    }dis[maxn];
    struct ed{int v,w,next;}edge[2*maxn];
    inline void init(int n) {
        tot=0;
        for(int i=1;i<=n;i++) head[i]=0,vis[i]=false;
    }
    inline void add_edge(int u,int v,int w) {
        edge[++tot]=ed{v,w,head[u]};
        head[u]=tot;
    }
    inline void dfs_size(int cur,int fa) {
        siz[cur]=1;
        for(int i=head[cur];i;i=edge[i].next) {
            if(edge[i].v!=fa && !vis[edge[i].v]) {
                dfs_size(edge[i].v,cur);
                siz[cur]+=siz[edge[i].v];
            }
        }
    }
    inline void dfs_root(int cur,int fa,int all) {  
        int max_son=all-siz[cur];
        for(int i=head[cur];i;i=edge[i].next) {
            if(edge[i].v!=fa && !vis[edge[i].v]) {
                max_son=max(max_son,siz[edge[i].v]);
                dfs_root(edge[i].v,cur,all);
            }
        }
        if(max_son<min_son) min_son=max_son,root=cur;
    }
    inline void dfs_roote(int cur,int fa,int d,int h) {
        dis[++num]=data(d,h);
        for(int i=head[cur];i;i=edge[i].next) {
            if(edge[i].v!=fa && !vis[edge[i].v]) {
                dfs_roote(edge[i].v,cur,d+edge[i].w,h+1);
            }
        }
    }
    inline long long calc(int cur,int fa,int d,int h) {
        num=0;
        dfs_roote(cur,fa,d,h);
        sort(dis+1,dis+num+1);
        for(int i=1;i<=num;i++) c[i]=dis[i].a;
        build_tree(c,num);
        long long ans=0;int p=num;
        for(int i=1;i<=num;i++) {
            while(p>i && dis[p].b+dis[i].b>l) p--;
            if(p>i) ans+=query(1,president_tree::tot,k-dis[i].a,roo[i],roo[p]);
        }
        return ans;
    }
    inline long long solve(int cur) {
        min_son=0x3f3f3f3f;
        dfs_size(cur,0);
        dfs_root(cur,0,siz[cur]);
        vis[root]=true;
        long long ans=calc(root,0,0,0);
        for(int i=head[root];i;i=edge[i].next) if(!vis[edge[i].v]) ans-=calc(edge[i].v,0,edge[i].w,1);
        for(int i=head[root];i;i=edge[i].next) if(!vis[edge[i].v]) ans+=solve(edge[i].v);
        return ans;
    }
}
using namespace point_divide_and_conquer;
int main() {
    while(~scanf("%d %d  %d",&n,&l,&k) && n) {
        init(n);
        for(int i=1,u,w;i<n;i++) {
            scanf("%d %d",&u,&w);
            add_edge(i+1,u,w);
            add_edge(u,i+1,w);
        }
        printf("%lld\n",solve(1));
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值