HEOI&TJOI2016 树

标准意义上第一道非模板树剖题,虽然我并不认为它是树剖

另:这是一道水题,第二道本省省选题(菜的一批)

Description

link

题意简述:给一棵树,支持两种操作:

1.对一个节点打标记

2.求一个节点距离最近的直系祖先(就是根到它链上的那种节点)

定义:自己也算自己的祖先

Solution

\[Begin\]

首先树和序列转化,求一波 \(dfn\) 序

整一棵线段树,维护这个序列

修改的时候就变成了了区间取 \(max\),这里不是那种纯 \(max\)

如果询问就变成了单点查询

这里有一个问题:我们在 \(push\_down\) (或者你叫它 \(spread\))的时候要取的是 \(max\),但是我们在 \(push\_up\) 的时候是维护的子树上面最小的深度编号

这样有点玄学剪枝的意味,但是正确性可以意会(毕竟是先取 \(max\) 再取 \(min\) )

\[Q.A.D\]

\(P.S.\) 博主知道是\(QED\)

(为啥我觉得不是树剖??)

Code


#include <bits/stdc++.h>
using namespace std;
#define int long long
namespace yspm {
inline int read() {
    int res = 0, f = 1;
    char k;
    while (!isdigit(k = getchar()))
        if (k == '-')
            f = -1;
    while (isdigit(k)) res = res * 10 + k - '0', k = getchar();
    return res * f;
}
const int N = 1e5 + 10;
struct node {
    int l, r, maxx, add;
#define add(p) t[p].add
#define l(p) t[p].l
#define r(p) t[p].r
#define maxx(p) t[p].maxx
} t[N << 2];
struct edge {
    int to, nxt;
} e[N << 1];
int head[N], cnt, tim, id[N], dep[N], sz[N], son[N], top[N], fa[N], n, T;
inline void adde(int u, int v) {
    e[++cnt].nxt = head[u];
    e[cnt].to = v;
    return head[u] = cnt, void();
}
inline void dfs1(int x, int f) {
    fa[x] = f;
    dep[x] = dep[f] + 1;
    sz[x] = 1;
    for (int i = head[x]; i; i = e[i].nxt) {
        int t = e[i].to;
        if (t == f)
            continue;
        dfs1(t, x);
        sz[x] += sz[t];
        if (sz[t] > sz[son[x]])
            son[x] = t;
    }
    return;
}
inline void dfs2(int x, int topf) {
    id[x] = ++tim;
    top[x] = topf;
    if (!son[x])
        return;
    dfs2(son[x], topf);
    for (int i = head[x]; i; i = e[i].nxt) {
        int t = e[i].to;
        if (t == fa[x] || t == son[x])
            continue;
        dfs2(t, t);
    }
    return;
}
inline void push_up(int p) {
    maxx(p) = dep[maxx(p << 1)] < dep[maxx(p << 1 | 1)] ? maxx(p << 1) : maxx(p << 1 | 1);
    return;
}
inline void spread(int p) {
    if (add(p)) {
        maxx(p << 1) = dep[maxx(p << 1)] > dep[add(p)] ? maxx(p << 1) : add(p);
        maxx(p << 1 | 1) = dep[maxx(p << 1 | 1)] > dep[add(p)] ? maxx(p << 1 | 1) : add(p);
        add(p << 1) = dep[add(p << 1)] > dep[add(p)] ? add(p << 1) : add(p);
        add(p << 1 | 1) = dep[add(p << 1 | 1)] > dep[add(p)] ? add(p << 1 | 1) : add(p);
    }
    return add(p) = 0, void();
}
inline void build(int p, int l, int r) {
    l(p) = l;
    r(p) = r;
    if (l == r)
        return;
    int mid = (l + r) >> 1;
    build(p << 1, l, mid);
    build(p << 1 | 1, mid + 1, r);
    return push_up(p);
}
inline void change(int p, int l, int r, int d) {
    if (l <= l(p) && r(p) <= r) {
        maxx(p) = dep[maxx(p)] > dep[d] ? maxx(p) : d;
        add(p) = dep[add(p)] > dep[d] ? add(p) : d;
        return void();
    }
    spread(p);
    int mid = (l(p) + r(p)) >> 1;
    if (l <= mid)
        if (dep[d] > dep[maxx(p << 1)])
            change(p << 1, l, r, d);
    if (r > mid)
        if (dep[d] > dep[maxx(p << 1 | 1)])
            change(p << 1 | 1, l, r, d);
    return push_up(p);
}
inline int query(int p, int x) {
    if (l(p) == x && r(p) == x)
        return maxx(p);
    spread(p);
    int mid = (l(p) + r(p)) >> 1, ans = -1;
    if (x <= mid)
        return query(p << 1, x);
    else
        return query(p << 1 | 1, x);
}
signed main() {
    n = read(), T = read();
    for (int i = 1, u, v; i < n; ++i) u = read(), v = read(), adde(u, v);
    dfs1(1, 0), dfs2(1, 1), build(1, 1, n);
    change(1, id[1], id[1] + sz[1] - 1, 1);
    while (T--) {
        string s;
        cin >> s;
        int x = read();
        if (s[0] == 'Q')
            printf("%lld\n", query(1, id[x]));
        if (s[0] == 'C')
            change(1, id[x], id[x] + sz[x] - 1, x);
    }
    return 0;
}
}  // namespace yspm
signed main() { return yspm::main();

原文地址:https://www.cnblogs.com/yspm/p/12382273.html

时间: 2024-10-30 12:06:59

HEOI&TJOI2016 树的相关文章

[Luogu 4092] HEOI/TJOI2016 树

[Luogu 4092] HEOI/TJOI2016 树 <题目链接> 搜了树剖标签不知道怎么就跳出了个暴搜题啊! 管他既然做了就发上来吧- 有修改标签就向下搜并修改,遇到标签即停止. 这题是真的真的短. #include <cstdio> #include <queue> using std::queue; const int MAXN=100010; bool flag[MAXN]; int n,q,cnt,head[MAXN],top[MAXN]; struct

[HEOI2016/TJOI2016]树

来一发大暴力 树链剖分无疑了 对于某个询问节点,二分答案所在的深度,若该深度到该节点上的区间和>0,说明其中有满足条件的点,增加深度继续二分,否则减小深度 线段树上的操作:单点修改+区间查询(区间和) 关于时间: 时间复杂度\(O(nlog^{2}n)\) 虽然不是最优解法,但能过了,稍微卡一下,总时间大概900ms,最大点300ms,如果\(O(nlogn)\)的玩家太注重卡常的话还是可以碾的,当然我的代码还有优化余地...(比如传参部分可以用空间换时间,卡常玩家可以尝试一下(还有fread之

P4092 [HEOI2016/TJOI2016]树

题目链接:https://www.luogu.org/problem/P4092 感觉这个题目和前面做的黑白染色的很像,思路都是差不多的吧. 1 #include <stdio.h> 2 #include <cstring> 3 #include <iostream> 4 #include <string> 5 #include <algorithm> 6 #include <queue> 7 #include <vector&

树链剖分(从入门到入土。)

前置知识:线段树,链式前向星,LCA,DFS序 树链剖分通常的操作: 1.x -> y 的路径上修改 2.x -> y 的路径上查询 3. 对于 x 的子树修改 4.对于 x 的子树查询. 一般还有换根操作.树剖也也可以做LCA. 树链剖分有两个DFS 这两个DFS就是把一棵树变成一个序列. 然后就可以用数据结构来维护了. 第一个DFS 用来求 \(fa\)(祖先节点) \(size\)(子树大小)\(son\)(重儿子) \(d\)(深度) 重儿子指的是\(size\)较大的儿子节点. 第二

bzoj4551[Tjoi2016&amp;Heoi2016]树

bzoj4551[Tjoi2016&Heoi2016]树 题意: 给个根节点为1的n点树,初始时节点1标记,Q个操作,每次可以标记一个点或求一个点最近一个标记了的祖先. 题解: 链剖可以写,当正解应该是并查集.离线读入所有操作,累加每个节点的标记次数,之后所有未被标记的节点向其父亲节点连边,然后倒着来,如果操作是询问则输出这个节点在并查集中的根节点,如果是标记则将该节点的标记数减1,一旦这个节点的标记数减到了0,就让它向父亲节点连边. 代码: 1 #include <cstdio> 2

bzoj4551【TJOI2016&amp;HEOI2016】树

4551: [Tjoi2016&Heoi2016]树 Time Limit: 20 Sec  Memory Limit: 128 MB Submit: 380  Solved: 234 [Submit][Status][Discuss] Description 在2016年,佳媛姐姐刚刚学习了树,非常开心.现在他想解决这样一个问题:给定一颗有根树(根为1),有以下 两种操作:1. 标记操作:对某个结点打上标记(在最开始,只有结点1有标记,其他结点均无标记,而且对于某个 结点,可以打多次标记.)2

【BZOJ4551】[Tjoi2016&amp;Heoi2016]树 并查集

[BZOJ4551][Tjoi2016&Heoi2016]树 Description 在2016年,佳媛姐姐刚刚学习了树,非常开心.现在他想解决这样一个问题:给定一颗有根树(根为1),有以下 两种操作:1. 标记操作:对某个结点打上标记(在最开始,只有结点1有标记,其他结点均无标记,而且对于某个 结点,可以打多次标记.)2. 询问操作:询问某个结点最近的一个打了标记的祖先(这个结点本身也算自己的祖 先)你能帮帮他吗? Input 输入第一行两个正整数N和Q分别表示节点个数和操作次数接下来N-1行

[BZOJ] 4552: [Tjoi2016&amp;Heoi2016]排序 #二分+线段树+算法设计策略

4552: [Tjoi2016&Heoi2016]排序 Time Limit: 60 Sec  Memory Limit: 256 MBSubmit: 1451  Solved: 734[Submit][Status][Discuss] Description 在2016年,佳媛姐姐喜欢上了数字序列.因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题 ,需要你来帮助他.这个难题是这样子的:给出一个1到n的全排列,现在对这个全排列序列进行m次局部排序,排 序分为两种:1:(0,l,r

【BZOJ4553】[Tjoi2016&amp;Heoi2016]序列 cdq分治+树状数组

[BZOJ4553][Tjoi2016&Heoi2016]序列 Description 佳媛姐姐过生日的时候,她的小伙伴从某宝上买了一个有趣的玩具送给他.玩具上有一个数列,数列中某些项的值可能会变化,但同一个时刻最多只有一个值发生变化.现在佳媛姐姐已经研究出了所有变化的可能性,她想请教你,能否选出一个子序列,使得在任意一种变化中,这个子序列都是不降的?请你告诉她这个子序列的最长长度即可.注意:每种变化最多只有一个值发生变化.在样例输入1中,所有的变化是: 1 2 3 2 2 3 1 3 3 1