BZOJ4538: [Hnoi2016]网络(树上路径交+整体二分)

本文介绍了一种高效的路径查询算法,通过使用线段树和ST表维护路径交集,实现快速查询路径交集是否包含特定节点的功能。算法适用于解决涉及大量路径查询的问题,如在图中寻找公共祖先等。

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

传送门

题解:
对于一个询问我们二分他的答案 k k ,那么只用判断所有大于k的路径的交集是否包含即可。

可以考虑整体二分,直接把路径一起往线段树上放,询问直接在线段树上二分就行了。

维护树上路径交可以做到 O(1) O ( 1 ) ,具体可以看这里
也可以直接用树状数组做路径加然后查询标记数, 不过时间复杂度会多一个 logn log ⁡ n

ST S T 表维护 LCA L C A ,总时间复杂度为 O(nlogn) O ( n log ⁡ n )

#include <bits/stdc++.h>
using namespace std;

inline int rd() {
    char ch=getchar(); int i=0,f=1;
    while(!isdigit(ch)) {if(ch=='-')f=-1; ch=getchar();}
    while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=getchar();}
    return i*f;
}
const int N=4e5+50;
int n,m,tot,dep[N],lst[N],lg[N];
int mn[N][19],cd,sd[N];
int dfn[N],od[N],sze[N],ind;
vector <int> edge[N];
struct data {
    int u,x,y,val,id;
    data(int u=0,int x=0,int y=0,int val=0):u(u),x(x),y(y),val(val){}
    friend inline bool operator <(const data &a,const data &b) {return a.val<b.val;}
} op[N],qry[N];
inline int getlca(int x,int y) {
    x=sd[x], y=sd[y];
    if(x>y) swap(x,y);
    int Lg=lg[y-x+1];
    return od[min(mn[x][Lg],mn[y-(1<<Lg)+1][Lg])];
}
struct chain {
    int a,b,lca;
    inline bool on(int x) {
        if(!lca) return true;
        if(lca==-1) return false;
        if(dfn[lca]>dfn[x] || lst[lca]<dfn[x]) return false;
        if(dfn[x]<=dfn[a] && lst[x]>=dfn[a]) return true;
        if(dfn[x]<=dfn[b] && lst[x]>=dfn[b]) return true;
        return false;
    }
    friend inline chain merge(chain a,chain b) {
        if(a.lca==-1 || b.lca==-1) return (chain){0,0,-1};
        if(!a.lca) return b;
        if(!b.lca) return a;
        if(dep[a.lca]>dep[b.lca]) swap(a,b);
        if(!a.on(b.lca)) return (chain){0,0,-1};
        if(a.lca==b.lca) {
            int ls=getlca(a.a,b.a), ls2=getlca(a.a,b.b), rs=getlca(a.b,b.b), rs2=getlca(a.b,b.a);
            if(dep[ls2]>dep[ls]) ls=ls2;
            if(dep[rs2]>dep[rs]) rs=rs2;
            return (chain){ls,rs,a.lca};
        } else {
            int ls=getlca(a.a,b.b), ls2=getlca(a.b,b.a), rs=getlca(a.a,b.a), rs2=getlca(a.b,b.b);
            if(dep[ls2]>dep[ls]) ls=ls2;
            if(dep[rs2]>dep[rs]) rs=rs2;
            return (chain){ls,rs,b.lca};
        }
    }
}path[N*2];
inline void dfs(int x,int f) {
    dfn[x]=++ind; sze[x]=1; dep[x]=dep[f]+1;
    od[ind]=x; mn[++cd][0]=dfn[x]; sd[x]=cd;
    for(int e=edge[x].size()-1;~e;e--) {
        int v=edge[x][e]; if(v==f) continue;
        dfs(v,x); sze[x]+=sze[v]; mn[++cd][0]=dfn[x];
    } lst[x]=dfn[x]+sze[x]-1;
}
inline void inc(int k,int l,int r,int p,chain t) {
    if(l==r) {path[k]=t; return;}
    int mid=(l+r)>>1;
    (p<=mid) ?inc(k<<1,l,mid,p,t): inc(k<<1|1,mid+1,r,p,t);
    path[k]=merge(path[k<<1],path[k<<1|1]);
}
inline int ask(int k,int l,int r,int x) {
    if(l>r) return -1; 
    if(l==r) {return !path[k].on(x)?op[l].val:-1;}
    int mid=(l+r)>>1;
    if(!path[k<<1|1].on(x)) return ask(k<<1|1,mid+1,r,x);
    else return ask(k<<1,l,mid,x);
}
int main() {
    n=rd(), m=rd();
    for(int i=1;i<n;i++) {
        int x=rd(), y=rd();
        edge[x].push_back(y);
        edge[y].push_back(x);
    } 
    dfs(1,0);
    lg[1]=0;
    for(int i=2;i<=cd;i++) lg[i]=lg[i>>1]+1;
    for(int i=1;i<=lg[cd];++i) 
        for(int j=1;j+(1<<i)-1<=cd;++j) 
            mn[j][i]=min(mn[j][i-1],mn[j+(1<<(i-1))][i-1]);
    for(int i=1;i<=m;i++) {
        qry[i].u=rd();
        if(!qry[i].u) {
            qry[i].x=rd(), qry[i].y=rd(), qry[i].val=rd();
            op[++tot]=qry[i]; op[tot].id=i;
        } else qry[i].x=rd(); 
    }
    sort(op+1,op+tot+1);
    for(int i=1;i<=tot;i++) qry[op[i].id].id=i;
    for(int i=1;i<=m;i++) {
        if(qry[i].u==0) inc(1,1,tot,qry[i].id,(chain){qry[i].x,qry[i].y,getlca(qry[i].x,qry[i].y)});
        else if(qry[i].u==1) inc(1,1,tot,qry[qry[i].x].id,(chain){0,0,0});
        else printf("%d\n",ask(1,1,tot,qry[i].x));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值