[SPOJ2798]QTREE3——[树链剖分]

该博客主要介绍了如何使用树链剖分解决一个包含两种操作(颜色改变和路径上黑点查询)的问题。在给定的树形结构中,博主提出使用异或操作来处理颜色变化,并利用区间最值线段树来维护路径上的第一个黑点信息。当节点颜色为白色时,将其设置为INF以避免在查询时产生影响。博客结论认为这是一道相对简单的题目。
摘要由CSDN通过智能技术生成

【题目描述】
给出N个点的一棵树(N-1条边),节点有白有黑,初始全为白。有两种操作:

0 i : 改变某点的颜色(原来是黑的变白,原来是白的变黑)
1 v : 询问1到v的路径上的第一个黑点,若无,输出-1

【输入格式】
第一行 N,Q,表示N个点和Q个操作
第二行到第N行N-1条无向边
再之后Q行,每行一个操作”0 i” 或者”1 v” (1 ≤ i, v ≤ N).

【输出格式】
对每个1 v操作输出结果

SampleInput S a m p l e I n p u t

9 8
1 2
1 3
2 4
2 9
5 9
7 9
8 9
6 8
1 3
0 8
1 6
1 7
0 2
1 9
0 2
1 9

SampleOutput S a m p l e O u t p u t

-1
8
-1
2
-1

【题意分析】
树上路径查询&&修改?果断上树剖。

修改颜色的时候用异或,这里线段树维护的是此区间内第一个出现的黑点。因为是单点修改,所以标记下传啥的都不需要~~直接上区间最值线段树。

如果白点的话就赋值INF,防止参与查询。
其他,好像也没什么可注意的了?

水题一道

Code:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cctype>
#include<cstring>
#include<vector>
#include<algorithm>
#define int long long
#define MAXN 120000
#define INF 1 << 30
using namespace std;

struct Front_Link_Star{
    int to,next;
}edge[MAXN << 1];

int tree[MAXN << 2],head[MAXN << 1],color[MAXN],id[MAXN],son[MAXN],depth[MAXN];
int father[MAXN],rk[MAXN],top[MAXN],size[MAXN],dfn,n,q,cnt;

inline void connect (int u,int v){
    edge[++cnt].to = v;
    edge[cnt].next = head[u];
    head[u] = cnt;
}

//上推
inline void push_up (int now){
    tree[now] = min (tree[now << 1],tree[now << 1 | 1]);
}

//建树
void build (int now,int tl,int tr){
    if (tl == tr){
        tree[now] = INF;
        return;
    }
    int mid = (tl + tr) >> 1;
    build (now << 1,tl,mid);
    build (now << 1 | 1,mid+1,tr);
    push_up (now);
}

//修改
void update (int now,int tl,int tr,int left,int right,int change){
    if (right < tl || tr < left)return;
    if (left <= tl && tr <= right){
        if (change == 1)tree[now] = tl;
        else tree[now] = INF;
        return;
    }
    int mid = (tl + tr) >> 1;
    update (now << 1,tl,mid,left,right,change);
    update (now << 1 | 1,mid+1,tr,left,right,change);
    push_up (now);
}

//查询
int query (int now,int tl,int tr,int left,int right){
    if (right < tl || tr < left)return INF;
    if (left <= tl && tr <= right)return tree[now];
    int res = INF,mid = (tl + tr) >> 1;
    res = min (query (now << 1,tl,mid,left,right),res);
    res = min (query (now << 1 | 1,mid+1,tr,left,right),res);
    return res;
}

//询问的是根节点开始的路径,所以交换什么的都不需要了~~
inline int Query_Range (int x){
    int tmp = INF;
    while (top[x] != 1){
        tmp = min (tmp,query (1,1,n,id[top[x]],id[x]));
        x = father[top[x]];
    }
    tmp = min (tmp,query (1,1,n,1,id[x]));
    return tmp;
}

//基本操作
void DFS1 (int now,int fa,int d){
    father[now] = fa;
    depth[now] = d;
    size[now] = 1;
    int maxson = -1;
    for (register int i = head[now];i;i = edge[i].next){
        int v = edge[i].to;
        if (v == fa)continue;
        DFS1 (v,now,d+1);
        size[now] += size[v];
        if (size[v] > maxson){
            maxson = size[v];
            son[now] = v;
        }
    }
}

void DFS2 (int now,int top_heavy){
    top[now] = top_heavy;
    id[now] = ++dfn;
    rk[dfn] = now;
    if (!son[now])return;
    DFS2 (son[now],top_heavy);
    for (register int i = head[now];i;i = edge[i].next){
        int v = edge[i].to;
        if (v != father[now] && v != son[now])DFS2 (v,v);
    }
}

signed main (){
    scanf ("%lld%lld",&n,&q);
    for (register int i = 1;i < n;i++){
        int x,y;
        scanf ("%lld%lld",&x,&y);
        connect (x,y);
        connect (y,x);
    }
    DFS1 (1,0,1);
    DFS2 (1,1);
    build (1,1,n);
    while (q--){
        int type;
        scanf ("%lld",&type);
        if (type == 0){
            int x;
            scanf ("%lld",&x);
            color [x] ^= 1;//异或修改颜色
            update (1,1,n,id[x],id[x],color[x]);
        }
        if (type == 1){
            int x;
            scanf ("%lld",&x);
            int ans = Query_Range (x);
            if (ans == INF)puts ("-1");
            else printf ("%lld\n",rk[ans]);
            //输出后要映射一下!!
        }
    }
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值