#\(\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. wordcount实例

    scala的wordcount实例 package com.wondersgroup.myscala import scala.actors.{Actor, Future} import scala. ...

  2. 【docker】centos7 上拉取docker镜像,一直拉取不到,报错:Error response from daemon: Get https://registry-1.docker.io/v2/: net/http: request canceled while w

    镜像拉取一直报错: Error response from daemon: Get https://registry-1.docker.io/v2/: net/http: request cancel ...

  3. PIE SDK归一化水体指数法

    1.算法功能简介 归一化指数法(NDWI(Normalized Difference Water Index,归一化水指数)),用遥感影像的特定波段进行归一化差值处理,以凸显影像中的水体信息. 其表达 ...

  4. 1-Kong文章记录

    参考: https://www.cnblogs.com/duanxz/p/9770645.html 系列博客可参考: 开源API网关系统(Kong教程)入门到精通 https://www.cnblog ...

  5. open live writer安装以及代码高亮、折叠插件安装

    一.目的 方便在本地写博客,不用在浏览器上写. 二.open live writer的安装 下载open live writer 这是我的 链接:https://pan.baidu.com/s/1u8 ...

  6. 教你玩转Linux系统目录结构

    Linux 内核最初只是由芬兰人林纳斯·托瓦兹(Linus Torvalds)在赫尔辛基大学上学时出于个人爱好而编写的.Linux 是一套免费使用和自由传播的类 Unix 操作系统,是一个基于 POS ...

  7. c# 编码风格

    此内容为copy别人的,仅供自己参看.如有意见,麻烦通知我,谢谢 1. C# 代码风格要求 1.1注释 类型.属性.事件.方法.方法参数,根据需要添加注释. 如果类型.属性.事件.方法.方法参数的名称 ...

  8. 【Visio】亲测Visio2013激活,破解工具下载

    破解方法地址: https://blog.csdn.net/qq_38276669/article/details/85046615

  9. python基础知识(最基本)

    保留字(关键字)   False None True and as break class continue def elif else except finally for from global ...

  10. Android ListView显示不同样式的item

    先look图 我们再使用listview时,listview的item大多时候都是一种样式,在很多app中也很常见,但有时候根据需求,可能数据的数量不一样,同个类型的数据显示的位置不同,亦或者有的it ...