#\(\mathcal{\color{red}{Description}}\)

\(Link\)

给定一棵以\(1\)为根的树,有两种操作:

\(C: \ \ x\)给点\(x\)打上花标记。

\(Q: \ \ x\)求点\(x\)的最近被标记祖先(自身包括)

#\(\mathcal{\color{red}{Solution}}\)

这个题……操作水的很但是我还是没能秒掉,我们考虑对于一个节点,单纯考虑线段树的话好像并不可做。那么我们先来考虑简单的,对于一个点,如果要查找一个点的最近标记祖先,那我们不断查找他和他的\(top\)之间的节点即可,即不断进行跳链操作。那么我们线段树维护什么信息呢?首先要满足结合律,其次\(query\)的结果可以直接拿来用。所以我们考虑维护一段链条上面的深度最深的被标记节点。

int qmark(int u, int v){
int ans = -1 ;
while(top[u] != top[v]){
if (dep[top[u]] < dep[top[v]]) swap(u, v) ;
ans = query(1, 1, N, id[top[u]], id[u]) ;
if (ans != -1) return qnq[ans] ;
u = fa[top[u]];
}
if (dep[u] > dep[v]) swap(u, v) ;
return qnq[ query(1, 1, N, id[u], id[v]) ];
}

查询的时候直接查询当前节点和根节点之间即可。

#include<cstdio>
#include<cstring>
#include<iostream>
#define il inline
#define MAXN 400001
#define ls(x) x << 1
#define rs(x) x << 1 | 1
#define to(k) e[k].t using namespace std ;
char pt ;
int id[MAXN], s[MAXN + 1050], qnq[MAXN];
int i, a, b, tot, N, M, cnt;
int head[MAXN], fa[MAXN], hs[MAXN], top[MAXN], dep[MAXN], sub[MAXN] ;
struct node{
int t, next ;
}e[MAXN]; il void add(int u, int v){
e[++ cnt].t = v ;
e[cnt].next = head[u] ;
head[u] = cnt ;
}
il int qr(){
int k = 0;
char c = getchar() ;
while(!isdigit(c)) c = getchar() ;
while(isdigit(c)){
k = (k << 1) + (k << 3) + c - 48 ;
c = getchar() ;
}
return k ;
} il void p_u(int rt){
s[rt] = max(s[ls(rt)], s[rs(rt)]) ;
}
il void update(int rt, int l, int r, int ul, int ur){
if (ul <= l && ur >= r){
s[rt] = l ;
return ;
}
int mid = (l + r) >> 1 ;
if (ul <= mid) update(ls(rt), l, mid, ul, ur) ;
if (ur > mid) update(rs(rt), mid + 1, r, ul, ur) ;
p_u(rt) ;
}
il int query(int rt, int l, int r, int nl, int nr){
int res = -1 ;
if (nl <= l && nr >= r) {res = s[rt];return res ;}
int mid = (l + r) >> 1 ;
if (nl <= mid)res = max(res, query(ls(rt), l, mid, nl, nr)) ;
if (nr > mid)res = max(res, query(rs(rt), mid + 1, r, nl, nr)) ;
return res ;
} void dfs1(int now, int f, int deep){
fa[now] = f ;
dep[now] = deep ;
int hson = -1 ;
sub[now] = 1 ;
for(int k = head[now]; k ; k = e[k].next){
if(to(k) == f) continue ;
dfs1(to(k), now, deep + 1) ;
sub[now] += sub[to(k)] ;
if(hson < sub[to(k)]) hs[now] = to(k), hson = sub[to(k)] ;
}
}
void dfs2(int now, int tp){
id[now] = ++ tot;
top[now] = tp ;
qnq[tot] = now ;
if(!hs[now]) return ;
dfs2(hs[now], tp) ;
for(int k = head[now]; k ; k = e[k].next){
if(to(k) == fa[now] || to(k) == hs[now]) continue ;
dfs2(to(k), to(k)) ;
}
}
int qmark(int u, int v){
int ans = -1 ;
while(top[u] != top[v]){
if (dep[top[u]] < dep[top[v]]) swap(u, v) ;
ans = query(1, 1, N, id[top[u]], id[u]) ;
if (ans != -1) return qnq[ans] ;
u = fa[top[u]];
}
if (dep[u] > dep[v]) swap(u, v) ;
return qnq[ query(1, 1, N, id[u], id[v]) ];
} int main(){
cin >> N >> M ;
memset(s, -1, sizeof(s)) ;
for(i = 1; i < N; i ++){
a = qr(), b = qr() ;
add(a, b) ;
add(b, a) ;
}
dfs1(1, 0, 1) ;
dfs2(1, 1) ;
update(1, 1, N, 1, 1) ;
for(i = 1; i <= M; i ++){
cin >> pt ;
if (pt == 'Q'){
b = qr() ;
printf("%d\n", qmark(b, 1)) ;
}
else {
a = qr() ;
update(1, 1, N, id[a], id[a]) ;
}
}
}

[题解向] Luogu4092 [HEOI2016/TJOI2016]树的更多相关文章

  1. 题解 P4092 【[HEOI2016/TJOI2016]树】

    参考了皎月半洒花的博客 看到树想到树剖,由于要取距自己到根离自己最近的标记点,刚开始想到线段树里存节点深度,查询时返回最大值.但是这样的话只能得到节点深度,无法得知节点编号,就想倍增乱搞一下,求出标记 ...

  2. luogu题解 P4092 【[HEOI2016/TJOI2016]树】树链剖分

    题目链接: https://www.luogu.org/problemnew/show/P4092 瞎扯--\(O(Q \log^3 N)\)解法 这道先yy出了一个\(O(Q \log^3 N)\) ...

  3. [HEOI2016/TJOI2016]树

    [HEOI2016/TJOI2016]树 思路 做的时候也是糊里糊涂的 就是求最大值的线段树 错误 线段树写错了 #include <bits/stdc++.h> #define FOR( ...

  4. 【题解】P4091 [HEOI2016/TJOI2016]求和

    [题解]P4091 [HEOI2016/TJOI2016]求和 [P4091 HEOI2016/TJOI2016]求和 可以知道\(i,j\)从\(0\)开始是可以的,因为这个时候等于\(0\).这种 ...

  5. 洛谷P4092 [HEOI2016/TJOI2016]树 并查集/树链剖分+线段树

    正解:并查集/树链剖分+线段树 解题报告: 传送门 感觉并查集的那个方法挺妙的,,,刚好又要复习下树剖了,所以就写个题解好了QwQ 首先说下并查集的方法趴QwQ 首先离线,读入所有操作,然后dfs遍历 ...

  6. [洛谷P4092][HEOI2016/TJOI2016]树

    题目大意:给你一棵树,有两个操作: $C\;x:$给第$x$个节点打上标记 $Q\;x:$询问第$x$个节点的祖先中最近的打过标记的点(自己也是自己的祖先) 题解:树剖,可以维护区间或,然后若一段区间 ...

  7. 题解 P4093 【[HEOI2016/TJOI2016]序列】

    这道题原来很水的? noteskey 一开始以为是顺序的 m 个修改,然后选出一段最长子序列使得每次修改后都满足不降 这 TM 根本不可做啊! 于是就去看题解了,然后看到转移要满足的条件的我发出了黑人 ...

  8. 暴力 【p4092】[HEOI2016/TJOI2016]树

    Description 在2016年,佳媛姐姐刚刚学习了树,非常开心.现在他想解决这样一个问题:给定一颗有根树(根为1),有以下两种操作: 标记操作:对某个结点打上标记(在最开始,只有结点1有标记,其 ...

  9. 洛谷 P4092 [HEOI2016/TJOI2016]树 || bzoj4551

    https://www.lydsy.com/JudgeOnline/problem.php?id=4551 https://www.luogu.org/problemnew/show/P4092 这当 ...

随机推荐

  1. 【MySQL】MariaDB10.2新特性--Flashback

    MariaDB10.2新特性--Flashback Flashback可以回滚到旧的数据,用于解决用户误删除数据的问题. 实战例子 MariaDB [zsd]> select * from te ...

  2. Ansible16:Playbook高级用法

    目录 本地执行 任务委托 任务暂停 滚动执行 只执行一次 设置环境变量 交互式提示 本地执行 如果希望在控制主机本地运行一个特定的任务,可以使用local_action语句. 假设我们需要配置的远程主 ...

  3. 单片机成长之路(51基础篇)- 024 基于 N76E003 的按键按键状态机

    前端时间要用按键,搞了个状态机(如图): main.c #include <N76E003.H> #include <SFR_Macro.h> //N76E003 SFR寄存器 ...

  4. CountDownEvent 信号类来等待直到一定数量的操作完成

    当主程序启动时,创建一个 CountDownEvent 类的实例,在其构造函数中指定个数操作完成发出信号,当前为2个操作完成会发出信号. /// <summary> /// 创建 Coun ...

  5. 记录RFID操作错误

    如果代码操作不了RFID设备,查看下通信协议的设置

  6. Python【day 9】函数入门2

    本节内容:1. 什么是函数2. 函数定义, 函数名, 函数体以及函数的调⽤3. 函数的返回值4. 函数的参数 一.什么是函数 我们可以先去定义一个事情或者功能(接口.服务.函数.功能). 等到需要的时 ...

  7. Spark GraphX图计算核心算子实战【AggreagteMessage】

    一.简介 参考博客:https://www.cnblogs.com/yszd/p/10186556.html 二.代码实现 package graphx import org.apache.log4j ...

  8. Vue组件间通信6种方式

    摘要: 总有一款合适的通信方式. 作者:浪里行舟 Fundebug经授权转载,版权归原作者所有. 前言 组件是 vue.js 最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的 ...

  9. 【爬虫】网页抓包工具--Fiddler--Request和Response

    [爬虫]网页抓包工具--Fiddler Fiddler基础知识 Fiddler是强大的抓包工具,它的原理是以web代理服务器的形式进行工作的,使用的代理地址是:127.0.0.1,端口默认为8888, ...

  10. OEL7.6安装Oracle Database 19C(VERSION 19.3.0.0)

    1.eDelivery中下载Oracle Database 19C和Oel的安装介质,并安装好操作系统 2.安装Oracle环境准备工具 环境准备工具会自动完成用户和用户组的创建.系统参数配置.依赖包 ...