#\(\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. 集合类源码(二)Collection之List(ArrayList, LinkedList, Vector)

    ArrayList 功能 完全命名 public class ArrayList<E> extends AbstractList<E> implements List<E ...

  2. sentry之二:sentry配置钉钉和email

    springboot+springcloud后台项目配置日志采集 gradle: compile 'com.getsentry.raven:raven-logback:8.0.2' compile ' ...

  3. SpringMVC-方法四种类型返回值总结,你用过几种?

    SpringMVC 现在算是 Java 领域的一个基础性框架了,很多人天天用,可是对于 SpringMVC 方法的返回值,你又是否完全清楚呢?今天松哥就来和大家聊一聊 SpringMVC 中四种不同类 ...

  4. 【CF285E】Positions in Permutations(动态规划,容斥)

    [CF285E]Positions in Permutations(动态规划,容斥) 题面 CF 洛谷 题解 首先发现恰好很不好算,所以转成至少,这样子只需要确定完一部分数之后剩下随意补. 然后套一个 ...

  5. windows10下录屏

    windows10自带了录屏功能.运行win+G即可打开.如果出现错误,可以运行如下PS脚本. https://files.cnblogs.com/files/mqingqing123/reinsta ...

  6. [转] vue前端异常监控sentry实践

    1. 监控原理 1.1 onerror 传统的前端监控原理分为异常捕获和异常上报.一般使用onerror捕获前端错误: window.onerror = (msg, url, line, col, e ...

  7. 13、VUE单文件工程

    1.为什么要使用单文件工程? 1.Vue.js路由组件的不方便 不支持引用HTML页面,以至于template里面定义的标签会编辑器当字符串,这让编辑变的困难. 2.Vue.js于Node.js语言结 ...

  8. MySQL、HBase、ES的对比

    hbase是列数据库,是kv结构的,ES的基于Lucene的搜索引擎的面向文档数据库吧 ES是搜索引擎,主要的优势在于快速搜索,HBase是数据库,优势在于存储数据,侧重点不同 MySQL:关系型数据 ...

  9. ASP.NET Core 3.0 解决无法将“Add-Migration”项识别为 cmdlet、函数、脚本文件或可运行程序的名称错误

    写在前面 在 ASP.NET Core 的项目中 使用 CodeFirst 的模式,进行初始化迁移时.出现如图所示的问题: 在度娘哪里查了半天之后,才从这个帖子里找到了答案.传送门 分析原因 ASP. ...

  10. [Leetcode] 5279. Subtract the Product and Sum of Digits of an Integer

    class Solution { public int subtractProductAndSum(int n) { int productResult = 1; int sumResult = 0; ...