P4116 Qtree3 树链剖分+线段树

该博客详细介绍了如何利用树链剖分和线段树解决P4116问题。通过树链剖分进行节点划分,然后运用线段树维护区间内特定点出现的最小位置,注意处理好下标转换。

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

传送门

思路:树链剖分后用线段树维护区间中黑点出现的位置的最小值,注意下标的转换就好了

代码:

#include <iostream>
#include <string>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long ll;
const int N = 1e5 + 10;

struct edge
{
    int to, next;
}Edge[N<<1];

int n, a[N], cnt, head[N], MIN[N<<2];
int top[N], son[N], f[N], size[N], id[N], rk[N], dep[N];

void add_edge(int from,int to)
{
    Edge[++cnt].to = to;
    Edge[cnt].next = head[from];
    head[from] = cnt;
}

void dfs1(int v,int fa,int depth)
{
    size[v] = 1;
    f[v] = fa;
    dep[v] = depth;
    for(int i = head[v]; i; i = Edge[i].next){
        int to = Edge[i].to;
        if(to == fa) continue;
        dfs1(to,v,depth+1);
        size[v] += size[to];
        if(size[to] > size[son[v]])
            son[v] = to;
    }
}

void dfs2(int v,int tp)
{
    top[v] = tp;
    id[v] = ++cnt;
    rk[cnt] = v;
    if(!son[v]) return ;
    dfs2(son[v],tp);
    for(int i = head[v]; i; i = Edge[i].next){
        int to = Edge[i].to;
        if(to == son[v] || to == f[v]) continue;
        dfs2(to,to);
    }
}

void pushUp(int rt)
{
    MIN[rt] = min(MIN[rt<<1], MIN[rt<<1|1]);
}

void build(int l,int r,int rt)
{
    if(l==r){
        MIN[rt] = N;	//N代表该区间中不存在黑点
        return ;
    }
    int m = (l+r)>>1;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
    pushUp(rt);
}

void update(int L,int l,int r,int rt)
{
    if(l == r){
    	if(MIN[rt] == N) MIN[rt] = l; //由于是输出离根节点最近的点,于是直接返回点在线段树中的编号
    	else	MIN[rt] = N;		//因为对同一条链上的节点来说,在新的线段树中的节点编号越小,离根节点越近
        return ;
    }
    int m = (l+r)>>1;
    if(L <= m) update(L,l,m,rt<<1);
    else update(L,m+1,r,rt<<1|1);
    pushUp(rt);
}

int queryMin(int L,int R,int l,int r,int rt) 
{
    if(L <= l && r <= R)
        return MIN[rt];
    int m = (l+r)>>1, ans = N;
    if(L <= m) ans = min(ans, queryMin(L,R,l,m,rt<<1));
    if(m <  R) ans = min(ans, queryMin(L,R,m+1,r,rt<<1|1));
    return ans;
}

int queryMins(int A,int B)
{
    int ans = N;
    while(top[A] != top[B]){
        if(dep[top[A]] < dep[top[B]]) swap(A,B);
        ans = min(ans, queryMin(id[top[A]],id[A],1,n,1));
        A = f[top[A]];
    }
    if(id[A] > id[B]) swap(A,B);
    return min(ans, queryMin(id[A],id[B],1,n,1));
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    int m, oper, A, B;
    cin>>n>>m;
    for(int i = 0; i < n-1; ++i){
        cin>>A>>B;
        add_edge(A,B);
        add_edge(B,A);
    }
    cnt = 0;
    dfs1(1,0,0);
    dfs2(1,1);
    build(1,n,1);
    while(m--){
        cin>>oper>>A;
        if(oper == 0){
            update(id[A],1,n,1);
        }
	    else{
        	int t = queryMins(A,1);
			if(t == N) t = -1;
			if(t != -1) t = rk[t]; //记得转化为原来的编号
            cout<<t<<'\n';
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值