写在前面

把黑题看成蓝题结果想了老半天感觉不对劲

本题对于理解 S p l a y Splay Splay和 L C T LCT LCT结构具有至关重要的意义,值得反复思考。

可能因为我比较菜

题目思路

题目给定一个类似神经网络的东西,每个节点都具有激活层、三输入单输出,输出由输入的 1 1 1的个数决定, 1 1 1比 0 0 0多就输出 1 1 1。每次修改一个外界输入,要求求根节点的输出。

首先考虑一个问题,修改外界输入对上级节点输出的影响究竟有几种:

  1. 从叶子节点开始有一段连续的输入 1 1 1的个数为 1 1 1的节点
    不难发现,此时如果将 12 12 12号节点修改为 1 1 1,那么因此的改变到 3 3 3号点(第一个非 1 1 1点)终止,不会引起根节点的修改
  2. 同样,从叶子节点开始有一段连续的输入 1 1 1个数为 2 2 2的节点,此时叶节点的改变只会影响到第一个非 2 2 2点

那么就可以解决这个问题了,对于每个修改的叶子节点,找到向上首个非 1 1 1/非 2 2 2节点(对应叶节点 0 → 1 0 \rightarrow 1 0→1和 1 → 0 1 \rightarrow 0 1→0)。

考虑使用LCT维护这个信息,对每个节点记录两个变量 i d 1 id_1 id1​和 i d 2 id_2 id2​,分别表示沿当前点向上的第一个非 1 1 1/非 2 2 2点。然后每次修改前 A c c e s s ( x ) Access(x) Access(x)再 S p l a y ( x ) Splay(x) Splay(x)就可以将信息上传到 x x x待修改的 x x x节点上。

但是注意,这里的 x x x节点并不是输入的那个叶子节点,而是它的父亲节点,因为叶子节点没有输入只有输出,修改是没有意义的。

那么问题就来了,信息该怎样上传到 x x x节点呢?我们回想 L C T LCT LCT的三个关键操作: R o t a t e ( x ) , S p l a y ( x ) , A c c e s s ( x ) Rotate(x), Splay(x), Access(x) Rotate(x),Splay(x),Access(x),我们可以以上树为例来理解:

我们建树时全部连虚边(只连父不连子),然后执行 A c c e s s ( 12 ) Access(12) Access(12),这时 1 ∼ 12 1 \sim 12 1∼12的点全部位于一条实链上,然后我们将 12 12 12旋到根上,在这里需要注意,我们看一下 R o t a t e ( ) Rotate() Rotate()和 S p l a y ( ) Splay() Splay()两个函数:

void rotate(int x) {int y = f[x], z = f[y], k = get(x);if(!isRoot(y)) ch[z][ch[z][1] == y] = x;ch[y][k] = ch[x][!k], f[ch[x][!k]] = y;ch[x][!k] = y, f[y] = x, f[x] = z;push_up(y); push_up(x);
}void update(int x) {if(!isRoot(x)) update(f[x]);push_down(x);
}void splay(int x) {update(x);for(int fa = f[x]; !isRoot(x); rotate(x), fa = f[x]) {if(!isRoot(fa)) rotate(get(fa) == get(x) ? fa : x);}push_up(x);
}

每次 R o t a t e Rotate Rotate后,我们会先更新原先的父亲节点的信息,再更新当前节点的信息。那么在一路将 12 12 12旋到根的过程中,我们会将到根节点路径上的点旋到 x x x的子树里,并将信息更新给正在旋转过程中的 x x x。那么我们考虑如何能够找到我们需要的信息:向上首个非 1 1 1/非 2 2 2节点,那么这些信息一定存在于其父亲节点(此处的父亲节点指的是旋转前的父亲节点)的右子树(旋转后)(感性理解, x x x的左子树为深度严格小于 x x x的节点,左子树首个节点的右子树在仍然满足深度严格小于 x x x的同时深度继续增大),因此我们优先从右子树继承信息,当右子树没有信息时判断当前节点是否满足,否则才从左子节点继承信息(此时更新的一般是 x x x节点本身,而不是其父亲节点)。

此外,本题细节比较多,要仔细写。

Code

#include <bits/stdc++.h>
#pragma gcc optimize("O2")
#pragma g++ optimize("O2")
#define int long long
#define endl '\n'
using namespace std;const int N = 3e6 + 10;int n = 0;namespace LCT{struct Info{short in, out;int id1, id2, add_tag;void activate() { out = (in >= 2); }}tree[N];int ch[N][2], f[N], tag[N];#define ls ch[x][0]#define rs ch[x][1]inline void push_reverse(int x) { swap(ls, rs), tag[x] ^= 1; }inline void push_add(int x, int c) {tree[x].in += c;tree[x].add_tag += c;tree[x].activate();swap(tree[x].id1, tree[x].id2);}inline void push_down(int x) {if(tag[x]) {if(ls) push_reverse(ls);if(rs) push_reverse(rs);tag[x] = 0;}if (tree[x].add_tag) {if(ls) push_add(ls, tree[x].add_tag);if(rs) push_add(rs, tree[x].add_tag);tree[x].add_tag = 0;}} inline void push_up(int x) {tree[x].id1 = tree[rs].id1;tree[x].id2 = tree[rs].id2;if(!tree[x].id1) {if(tree[x].in != 1) tree[x].id1 = x;else tree[x].id1 = tree[ls].id1;}if(!tree[x].id2) {if(tree[x].in != 2) tree[x].id2 = x;else tree[x].id2 = tree[ls].id2;}}#define get(x) (ch[f[x]][1] == x)                        #define isRoot(x) (ch[f[x]][0] != x && ch[f[x]][1] != x)void rotate(int x) {int y = f[x], z = f[y], k = get(x);if(!isRoot(y)) ch[z][ch[z][1] == y] = x;ch[y][k] = ch[x][!k], f[ch[x][!k]] = y;ch[x][!k] = y, f[y] = x, f[x] = z;push_up(y); push_up(x);}void update(int x) {if(!isRoot(x)) update(f[x]);push_down(x);}void splay(int x) {update(x);for(int fa = f[x]; !isRoot(x); rotate(x), fa = f[x]) {if(!isRoot(fa)) rotate(get(fa) == get(x) ? fa : x);}push_up(x);}int access(int x) {int p;for(p = 0; x; x = f[p = x]) splay(x), rs = p, push_up(x);return p;}void makeRoot(int x) { access(x), splay(x), push_reverse(x); }int findRoot(int x) {access(x), splay(x);while(ch[x][0]) push_down(x), x = ch[x][0];splay(x);return x;}void split(int x, int y) {makeRoot(x);access(y), splay(y);}void link(int x, int y) {makeRoot(x);if(findRoot(y) != x) f[x] = y;}void cut(int x, int y) {makeRoot(x);if(findRoot(y) == x && f[y] == x && !ch[y][0]) {f[y] = ch[x][1] = 0;push_up(x);}}
}using LCT::tree;vector<int> g[N];void dfs(int u) {for(auto v : g[u]) {dfs(v);tree[u].in += tree[v].out;}if(u <= n) tree[u].activate();// cout << "The output of node " << u << " is " << tree[u].out << endl;
}inline void solve(){cin >> n;for(int i = 1; i <= n; i++) {int x1, x2, x3; cin >> x1 >> x2 >> x3;g[i].emplace_back(x1), LCT::f[x1] = i;g[i].emplace_back(x2), LCT::f[x2] = i;g[i].emplace_back(x3), LCT::f[x3] = i;}for(int i = n + 1; i <= 3 * n + 1; i++) cin >> tree[i].out;dfs(1);int q, ans = tree[1].out; cin >> q;while(q--) {int x; cin >> x;int xfa = LCT::f[x];// cout << "Query of change : input x = " << x << ", father of x is " << xfa << endl;  int add_val = (tree[x].out == 1) ? -1 : 1;LCT::access(xfa), LCT::splay(xfa);int split_node = (tree[x].out == 1) ? tree[xfa].id2 : tree[xfa].id1;// cout << "Deepest node of found : " << split_node << endl;// cout << "Add_val = " << add_val << endl; if(split_node) {LCT::splay(split_node);LCT::push_add(LCT::ch[split_node][1], add_val);// cout << "Tree[" << split_node << "].in origin value is " << tree[split_node].in << endl;tree[split_node].in += add_val;tree[split_node].activate();LCT::push_up(LCT::ch[split_node][1]);// cout << "Tree[" << split_node << "].in has been changed to " << tree[split_node].in << endl;// ans = tree[1].out;} else {LCT::push_add(xfa, add_val);LCT::push_up(xfa);ans ^= 1;}tree[x].out ^= 1;cout << ans << endl;}
}signed main(){ios_base::sync_with_stdio(false), cin.tie(0);int t = 1; // cin >> t;while(t--) solve();return 0;
}

[LCT刷题][树链信息维护] P4332 [SHOI2014]三叉神经树相关推荐

  1. P4332 [SHOI2014]三叉神经树(LCT)

    Luogu4332 LOJ2187 题解 代码-Tea 题意 : 每个点有三个儿子 , 给定叶节点的权值\(0\)或\(1\)且支持修改 , 非叶子节点的权值为当有\(>=2\)个儿子的权值为\ ...

  2. 树链剖分(bzoj 1036: [ZJOI2008]树的统计Count)

    树链剖分: 把一棵树剖分为若干条链,然后利用数据结构(树状数组,SBT,Splay,线段树等等)去维护每一条链,复杂度 为O(logn),总体复杂度O(nlog²n) 步骤: ①将树的边分成重边和轻边 ...

  3. 力扣(LeetCode)打卡刷题交流计划(长期维护)

    前言 忙忙活活暑期过去了一半,在即将升学的日子里,打算干点什么东西,由于实力以及经验的欠缺没有创群和大家讨论,但我更喜欢以更实在的方式--能作点什么--和大家一起从0打开力扣LeetCode, 对,没 ...

  4. 【宫水三叶的刷题日记】675. 为高尔夫比赛砍树(困难)

    题目描述 这是 LeetCode 上的 675. 为高尔夫比赛砍树 ,难度为 困难. Tag : 「图论 BFS」.「AStar 算法」.「启发式搜索」.「并查集」 你被请来给一个要举办高尔夫比赛的树 ...

  5. 【树链剖分】「ZJOI2008」树的统计

    前置知识 树链剖分的思想及能解决的问题 树链剖分用于将树分割成若干条链的形式,以维护树上路径的信息. 具体来说,将整棵树剖分为若干条链,使它组合成线性结构,然后用其他的数据结构维护信息. 树链剖分(树 ...

  6. 【树链剖分】【模板】树的统计(P2590)

    小目录 链接 题目描述 样例输入 样例输出 思路 代码 链接 Luogu P2590 题目描述 给出一棵树,对该树进行单点修改,区间查询最大值,区间求和 样例输入 4 1 2 2 3 4 1 4 2 ...

  7. bzoj 4771: 七彩树 树链的并+可持久化线段树

    题目大意: 给定一颗树,询问树中某个点x的子树中与其距离不超过d的所有点中本质不同的颜色数 强制在线 题解: 一下午终于把这道题叉掉了. 写了三个算法,前两个都是错的,后一个是%的网上大爷们的题解. ...

  8. [LCT刷题][连通性维护] P3950 部落冲突

    链接:https://www.luogu.com.cn/problem/P3950 题目思路 没思路,太板了,一眼LCT 对于开战执行 c u t cut cut,停战 l i n k link li ...

  9. 刷题总结——序列操作(权值线段树套树状数组)

    题目: 题目描述 给出序列 a1,a2,-,an(0≤ai≤109),有关序列的两种操作. 1. ai(1≤i≤n)变成 x(0≤x≤109). 2. 求 al,al+1,-,ar(1≤l≤r≤n)第 ...

最新文章

  1. 测试ATS对某个特定域名下文件处理效果的方法
  2. 2.最详细的WSDD配置文件注释
  3. go项目部署到linux服务器
  4. 蓝桥杯九宫重排(bfs+用set去重)
  5. 移植MT76x8 私有WIFI驱动V4.1.0.0到Openwrt 18.06所遇到的坑
  6. Linux 命令之 type -- 显示指定命令的类型
  7. Spring Cloud OpenFeign夺命连环9问,这谁受得了?
  8. 苹果6系统怎么更新不了_苹果App Store登录不了怎么办_登录不了App Store的解决办法?...
  9. 《HTML5 从入门到精通--7.6.3 单元格垂直跨度——rowspan》
  10. 判斷指定資料庫存取權
  11. 四针角oled屏连接arduino_和孩子一起做个显示温湿度的oled屏桌面摆件[青创小电视]...
  12. Scalaz(49)- scalaz-stream: 深入了解-Sink/Channel
  13. tcpudp测试工具的使用,串口转wifi模块udp测试
  14. python opencv 获取图片清晰度
  15. 功率因数 matlab,基于MATLAB的有源功率因数校正器设计
  16. android 夜间模式功能,Android 夜间模式的三种实现
  17. centos6.5安装openoffice和swftools 错误信息
  18. 华为手机刷微博体验更好?技术角度的分析和思考,Android基础72问
  19. 2023年电工杯数学建模B题 人工智能对大学生学习影响的评价 具体建模过程 代码以及思路
  20. 【xsy2440】【GDOI2016】疯狂动物城

热门文章

  1. 关于error: The following untracked working tree files would be overwritten by checkout的解决方案
  2. 软件测试 - 项目实战篇
  3. jQuery——遍历
  4. Web基础(从入门到项目实战)
  5. js基础-event对象
  6. MVVM+Retrofit+Kotlin网络框架封装
  7. 高等学校工程教育改革试点的新动向
  8. Linux配置MySQL远程访问权限
  9. Redis高负载排查记录
  10. shell脚本之n之内的质数