题目描述

在2016年,佳媛姐姐刚刚学习了树,非常开心。现在他想解决这样一个问题:给定一颗有根树(根为1),有以下两种操作:

  1. 标记操作:对某个结点打上标记(在最开始,只有结点1有标记,其他结点均无标记,而且对于某个结点,可以打多次标记。)

  2. 询问操作:询问某个结点最近的一个打了标记的祖先(这个结点本身也算自己的祖先)

你能帮帮他吗?

输入输出格式

输入格式:

输入第一行两个正整数N和Q分别表示节点个数和操作次数

接下来N-1行,每行两个正整数u,v(1≤u,v≤n)表示u到v有一条有向边

接下来Q行,形如“opernum”oper为“C”时表示这是一个标记操作,oper为“Q”时表示这是一个询问操作对于每次询问操作。

输出格式:

输出一个正整数,表示结果

输入输出样例

输入样例#1: 复制

5 5
1 2
1 3
2 4
2 5
Q 2
C 2
Q 2
Q 5
Q 3
输出样例#1: 复制

1
2
2
1

说明

30%的数据,1 ≤ N, Q ≤ 1000

70%的数据,1 ≤ N, Q ≤ 10000

100%的数据,1 ≤ N, Q ≤ 100000

// luogu-judger-enable-o2
//其实不是树剖,而是dfs序+线段树
//每个点的dfs序是一段区间,这段区间就是它的孩子们
//当我们更改一个点的时候,就把它的孩子们全改了 (要判断一下改不改)
//如果它的儿子们的num(要求的祖先)的dep小于lazy的deep,就修改它的儿子们的lazy和num,改成当前点的lazy
//否则就不改 这样就避免了后来的标记覆盖了之前的标记 而且我们的标记一定是当前最优的 #include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std; const int N=1e5+; int n,m;
int opt,x;
int head[N],num_edge;
struct Edge
{
int v,nxt;
}edge[N<<];
struct Node
{
int fa,son;
int s,t;
int size;
int dep,top;
}node[N];
struct TREE
{
TREE *lson,*rson;
int l,r,mid;
int num,lazy;
}tree[N<<]; typedef TREE* Tree;
Tree Root,now_node=tree; inline int read()
{
char c=getchar();
for(;!isdigit(c);c=getchar())
if(c=='C') return ;
else if(c=='Q') return ;
int num=;
for(;isdigit(c);c=getchar())
num=num*+c-'';
return num;
} inline void add_edge(int u,int v)
{
edge[++num_edge].v=v;
edge[num_edge].nxt=head[u];
head[u]=num_edge;
} void dfs1(int u)
{
node[u].size=;
for(int i=head[u],v;i;i=edge[i].nxt)
{
v=edge[i].v;
if(v==node[u].fa)
continue;
node[v].fa=u;
node[v].dep=node[u].dep+;
dfs1(v);
node[u].size+=node[v].size;
if(node[v].size>node[node[u].son].size)
node[u].son=v;
}
} int bound;
void dfs2(int u)
{
node[u].s=++bound;
if(node[u].son)
{
dfs2(node[u].son);
for(int i=head[u],v;i;i=edge[i].nxt)
{
v=edge[i].v;
if(v==node[u].fa||v==node[u].son)
continue;
dfs2(v);
}
}
node[u].t=bound;
} void build(Tree &root,int l,int r)
{
root=++now_node;
root->l=l,root->r=r,root->mid=l+r>>;
root->num=;
if(l==r)
return;
build(root->lson,l,root->mid);
build(root->rson,root->mid+,r);
} inline void pushdown(Tree root)
{
if(root->lazy)
{
if(node[root->lazy].dep>node[root->lson->lazy].dep)
root->lson->lazy=root->lazy;
if(node[root->lazy].dep>node[root->rson->lazy].dep)
root->rson->lazy=root->lazy;
if(node[root->lazy].dep>node[root->lson->num].dep)
root->lson->num=root->lazy;
if(node[root->lazy].dep>node[root->rson->num].dep)
root->rson->num=root->lazy;
root->lazy=;
}
} void update(Tree root,int l,int r,int val)
{
if(l==root->l&&r==root->r)
{
root->num=node[root->num].dep>node[val].dep?root->num:val; //这儿也要比较一下的,一开始没比较,直接改的
root->lazy=node[root->lazy].dep>node[val].dep?root->lazy:val; //因为这个区间不一定只改一次,所以也要比较 当时抽了
return;
}
pushdown(root);
if(r<=root->mid)
update(root->lson,l,r,val);
else if(l>root->mid)
update(root->rson,l,r,val);
else
{
update(root->lson,l,root->mid,val);
update(root->rson,root->mid+,r,val);
}
} int query(Tree root,int pos)
{
if(root->l==root->r)
return root->num;
pushdown(root);
if(pos<=root->mid)
return query(root->lson,pos);
else
return query(root->rson,pos);
} int main()
{
n=read(),m=read();
for(int i=,u,v;i<n;++i)
{
u=read(),v=read();
add_edge(u,v);
add_edge(v,u);
}
dfs1();
dfs2();
build(Root,,n);
for(int i=;i<=m;++i)
{
opt=read(),x=read();
if(opt==)
update(Root,node[x].s,node[x].t,x);
else
printf("%d\n",query(Root,node[x].s));
}
return ;
}

P4092 [HEOI2016/TJOI2016]树的更多相关文章

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

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

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

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

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

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

  4. [HEOI2016/TJOI2016]树

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

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

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

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

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

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

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

  8. [题解向] Luogu4092 [HEOI2016/TJOI2016]树

    #\(\mathcal{\color{red}{Description}}\) \(Link\) 给定一棵以\(1\)为根的树,有两种操作: \(C: \ \ x\)给点\(x\)打上花标记. \(Q ...

  9. [HEOI2016/TJOI2016]排序 线段树+二分

    [HEOI2016/TJOI2016]排序 内存限制:256 MiB 时间限制:6000 ms 标准输入输出 题目类型:传统 评测方式:文本比较 题目描述 在2016年,佳媛姐姐喜欢上了数字序列.因而 ...

随机推荐

  1. CentOS7+Docker+MangoDB下部署简单的MongoDB分片集群

    简单的在Docker上快速部署MongoDB分片集群 前言 文中使用的环境如下 OS:CentOS Linux release 7.5.1804 (Core) Docker:Docker versio ...

  2. Java CountDownLatch应用

    Java的concurrent包里面的CountDownLatch其实可以把它看作一个计数器,只不过这个计数器的操作是原子操作,同时只能有一个线程去操作这个计数器,也就是同时只能有一个线程去减这个计数 ...

  3. [BZOJ4755][JSOI2016]扭动的回文串(manacher+Hash)

    前两种情况显然直接manacher,对于第三种,枚举回文中心,二分回文半径,哈希判断即可. #include<cstdio> #include<algorithm> #defi ...

  4. springboot中将日志信息存放在catalina.base中

    <?xml version="1.0" encoding="UTF-8"?> <configuration debug="true& ...

  5. 在论坛中出现的比较难的sql问题:10(删除多表中的同一个外键)

    原文:在论坛中出现的比较难的sql问题:10(删除多表中的同一个外键) 最近,在论坛中,遇到了不少比较难的sql问题,虽然自己都能解决,但发现过几天后,就记不起来了,也忘记解决的方法了. 所以,觉得有 ...

  6. 自己用ansible加shell 写的自动安装kubernetes的脚本

    脚本地址:https://github.com/shatianxiaozi/auto_install_k8s.git 1. 下载 git clone https://github.com/shatia ...

  7. kvm第五章--虚拟迁移

  8. 打印html页面

    // 打印类属性.方法定义 const Print = function (dom, options) { if (!(this instanceof Print)) return new Print ...

  9. SPI简述

    SPI是器件的比较常用的通信协议. SPI总共有四根线: SS:片选线,每个设备都和主机MCU有一条单独片选线相连,片选线拉低意味主机输出,也就是说一个主机可以和多个从机相连,只需要有足够多的片选线. ...

  10. Factorization Machines