Distance on the tree (2019南昌网络赛)

本文介绍了一种结合主席树和LCA算法的高级数据结构应用,通过实例讲解如何使用这两种算法解决特定类型的问题,包括预处理和查询区间内元素的高效计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

主席树套LCA

题目链接:https://nanti.jisuanke.com/t/38229

预处理出lca的一套东西,将查询区间[L,R]分成两半,[L,LCA(L,R)],[R,LCA(L,R)] 计算区间内小于等于k的个数即可

主席树记录每一个子节点到初始节点间出现的所有线段长度的个数。

主席树这种东西的话,,,百度学吧。。。我讲不清,我太菜了。。

对于常见的主席树区间查询来说,查询区间[L,R]需要消掉L-1次操作带来的影响,是因为他每个点具有点权,而这道题是点与点之间具有边权,故消除影响只需要消除lca之前的影响。

 

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>

using namespace std;

typedef long long ll;

const int maxn=1e5+7;

int b[maxn];
int n,m;
void quchong(int n){
    sort(b+1,b+n);
    m=unique(b+1,b+n)-b-1;
}

int getid(int x){
    return lower_bound(b+1,b+1+m,x)-b;
}

const int maxm=2e5+7;
struct Edge{
    int v,w,next;
}edge[maxm];
int head[maxn],top;



void add(int u,int v,int w){
    edge[top].v=v;
    edge[top].w=w;
    edge[top].next=head[u];
    head[u]=top++;
}


struct Tree{
    int lc,rc;
    int sum;
}tree[2061118];
int tot;
int root[2061118];

void init(){
    memset(head,-1,sizeof(head));
    top=0;
    tot=0;
}

void pushup(int k){
    tree[k].sum=tree[tree[k].lc].sum+tree[tree[k].rc].sum;
}

int build(int l,int r){
    int k=++tot;
    if(l==r){
        tree[k].sum=0;
        return k;
    }
    int mid=(l+r)>>1;
    tree[k].lc=build(l,mid);
    tree[k].rc=build(mid+1,r);
    pushup(k);
    return k;
}

int updata(int now,int l,int r,int x,int val){
    int k=++tot;
    tree[k]=tree[now];
    if(l==r){
        ++tree[k].sum;
        return k;
    }
    int mid=(l+r)>>1;
    if(x<=mid) tree[k].lc=updata(tree[now].lc,l,mid,x,val);
    else tree[k].rc=updata(tree[now].rc,mid+1,r,x,val);
    pushup(k);
    return k;
}

int myfind(int p,int q,int l,int r,int L,int R){
    if(l>=L&&r<=R) return tree[p].sum-tree[q].sum;
    int mid=(l+r)>>1;
    int res=0;
    if(L<=mid) res+=myfind(tree[p].lc,tree[q].lc,l,mid,L,R);
    if(R>mid) res+=myfind(tree[p].rc,tree[q].rc,mid+1,r,L,R);
    return res;
}

int fa[maxn][29];
int mi=17;
int deap[maxn];
//ll dis[maxn];

queue<int> q;
void bfs(int st){
    while(!q.empty()) q.pop();
    q.push(st);
    deap[st]=1;
    //dis[st]=0;
    root[st]=build(1,m);


    int u,v;
    ll w;
    while(!q.empty()){
        u=q.front(); q.pop();
        for(int i=head[u];i!=-1;i=edge[i].next){
            v=edge[i].v;
            w=edge[i].w;
            if(deap[v]) continue;
            fa[v][0]=u;
            deap[v]=deap[u]+1;
            //dis[v]=dis[u]+w;
            for(int j=1;j<=mi;++j)
                fa[v][j]=fa[fa[v][j-1]][j-1];
            root[v]=updata(root[u],1,m,getid(w),1);
            q.push(v);

        }
    }
}

int LCA(int x,int y){
    if(deap[x]>deap[y]) swap(x,y);
    for(int i=mi;i>=0;--i)
        if(deap[fa[y][i]]>=deap[x]) y=fa[y][i];
    if(x==y) return x;
    for(int i=mi;i>=0;--i)
        if(fa[y][i]!=fa[x][i]){
            x=fa[x][i];
            y=fa[y][i];
        }
    return fa[x][0];
}

int idd(int x){
    int l=1,r=m,mid;
    while(l<=r){
        mid=(l+r)>>1;
        if(b[mid]>x) r=mid-1;
        else l=mid+1;
    }
    return r;
}
int main(){
    //cout<<(int)(maxn*4+maxn*log2(maxn))<<endl;
    init();
    int q;
    int u,v,w;
    scanf("%d%d",&n,&q);
    for(int i=1;i<n;++i){
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w);
        add(v,u,w);
        b[i]=w;
    }
    quchong(n);
    root[0]=build(1,m);
    bfs(1);
    while(q--){
        scanf("%d%d%d",&u,&v,&w);
        int id=idd(w);
        if(id==0) printf("0\n");
        else{
            int fre=LCA(u,v);
            int hh=fa[fre][0];
            //cout<<fa[fre][0]<<endl;
            printf("%d\n",myfind(root[v],root[fre],1,m,1,id)+myfind(root[u],root[fre],1,m,1,id));
        }

    }


    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值