「SHOI2014」三叉神经树

给你一颗由\(n\)个非叶子结点和\(2n+1\)个叶子结点构成的完全三叉树,每个叶子结点有一个输出:\(0\)或\(1\),每个非叶子结点的输出为自己的叶子结点中较多的那一种状态。

有\(q\)次修改操作,每次修改一个叶子结点的输出,求每次修改后根结点的输出。

\(n\leq 5\times 10^5,q\leq 5\times 10^5\).

不难发现每个节点有两种可能的情况:

​ 1.三个儿子输出均为\(0/1\)。

​ 2.两个儿子输出为\(0/1\),剩下一个儿子与之相反。

为了方便起见,我们简称第一种情况的点为1类点,第二种情况的点为2类点。

我们发现,一个点的输出被改变,只会影响它的连续若干位祖先。

同时,如果对1类点的任意一个儿子进行修改操作,并不会影响它自身的输出,同样也不会影响这个点的任何一个祖先,只是单纯的把这个点变成了2类点罢了。

而如果对2类点的一个儿子进行修改操作,则会有以下两种可能的结果:

  • 1.这个点变成了1类点。
  • 2.这个点的输出被改变了,并且这种改变还会向父亲传递,直到遇到第一个1类点或是输出与这个点不同的点。

我们定义以下状态:

  • 状态\(0\):输出\(0\)的2类点
  • 状态\(1\):输出\(1\)的2类点
  • 状态\(2\):1类点

这样问题就变成了求连续的最长\(0/1\)链,在序列上可以利用线段树上二分处理,而在树上我们则考虑利用树链剖分来进行维护。

当一个叶子结点输出修改后:

  • 如果父亲结点输出未改变,则父亲结点一定在1类点和2类点之间进行了转化,对应线段树上单点修改。
  • 如果父亲结点输出改变了,那么利用线段树二分自下而上找到被修改的祖先中深度最浅的。并且这一条链上所有的点都是2类点,在二分的过程中直接反转状态即可。

时间复杂度为\(\Omicron(n\log^2 n)\).

这里还要注意一个额外的细节,我们的线段树虽然维护了2类点的输出,但1类点的输出我们并不知晓,不过好在每次有关1类点的操作都是单点操作,所以我们可以额外开一个数组维护1类点的输出。

LCT做法挖坑!!!

code

#include<bits/stdc++.h>
using namespace std;
const int maxn=500005;
int n,q,fa[maxn*3],son[maxn][3],a[maxn*3];
int wss[maxn],topf[maxn],dfn[maxn],idx[maxn],dfs_clock,siz[maxn];
inline int read(){
int res=0,f_f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') f_f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') res=(res<<3)+(res<<1)+(ch-'0'),ch=getchar();
return res*f_f;
}
namespace SEG{
int tp[maxn<<2],rev[maxn<<2];
inline void push_up(int o){
int ls=o<<1,rs=o<<1|1;
if(tp[ls]!=tp[rs]||tp[ls]==2) tp[o]=2;
else tp[o]=tp[ls];
}
inline void modify(int o){
tp[o]^=1,rev[o]^=1;
}
inline void push_down(int o){
if(!rev[o]) return;
tp[o<<1]^=1,tp[o<<1|1]^=1,rev[o]=0;
rev[o<<1]^=1,rev[o<<1|1]^=1;
}
inline int update_tp(int o,int l,int r,int ql,int qr,int id){
int mid=l+r>>1,res;
if(ql<=l&&r<=qr){
if(tp[o]!=2){
if(tp[o]==id){
modify(o);
return l;
}
return 0;
}
if(l==r) return 0;
push_down(o);
res=update_tp(o<<1|1,mid+1,r,ql,qr,id);
if(res==mid+1){
int x=update_tp(o<<1,l,mid,ql,qr,id);
if(x) res=x;
}
push_up(o);
return res;
}
push_down(o);
if(qr<=mid) res=update_tp(o<<1,l,mid,ql,qr,id);
else if(ql>mid) res=update_tp(o<<1|1,mid+1,r,ql,qr,id);
else{
res=update_tp(o<<1|1,mid+1,r,ql,qr,id);
if(res==mid+1){
int x=update_tp(o<<1,l,mid,ql,qr,id);
if(x) res=x;
}
}
push_up(o);
return res;
}
inline void update_p(int o,int l,int r,int x,int v){
if(l==r&&l==x){
tp[o]=v;
return;
}
int mid=l+r>>1;
push_down(o);
if(x<=mid) update_p(o<<1,l,mid,x,v);
else update_p(o<<1|1,mid+1,r,x,v);
push_up(o);
}
inline int query(int o,int l,int r,int x){
if(l==r&&l==x) return tp[o];
int mid=l+r>>1;
push_down(o);
if(x<=mid) return query(o<<1,l,mid,x);
else return query(o<<1|1,mid+1,r,x);
}
}
inline void dfs1(int u,int cur){
siz[u]=1;
for (int i=0;i<3;i++){
int x=son[u][i];
if(!x) continue;
dfs1(x,u),siz[u]+=siz[x];
if(!wss[u]||siz[x]>siz[wss[u]]) wss[u]=x;
}
int res=(a[u]<=1)?0:1;
if(cur) a[cur]+=res;
}
inline void dfs2(int u,int cur){
dfn[u]=++dfs_clock,idx[dfs_clock]=u;
SEG::update_p(1,1,n,dfn[u],(a[u]!=0&&a[u]!=3)?a[u]-1:2);
topf[u]=cur,a[u]=(a[u]<=1)?0:1;
if(!wss[u]) return;
dfs2(wss[u],cur);
for (int i=0;i<3;i++){
int x=son[u][i];
if(!x||x==wss[u]) continue;
dfs2(x,x);
}
}
inline int update_range(int x,int id){
if(!x) return 0;
int res=SEG::update_tp(1,1,n,dfn[topf[x]],dfn[x],id);
if(res^dfn[topf[x]]) return res;
int cur=update_range(fa[topf[x]],id);
if(cur) return cur;
return res;
}
inline int get_p(int x){
int res=SEG::query(1,1,n,dfn[x]);
if(res^2) return res;
else return a[x];
}
int main(){
n=read();
for (int i=1;i<=n;i++){
for (int j=0;j<3;j++){
int x=read();
fa[x]=i;
if(x<=n) son[i][j]=x;
}
}
for (int i=n+1;i<=n*3+1;i++){
a[i]=read(),a[fa[i]]+=a[i];
}
dfs1(1,0),dfs2(1,1);
q=read();
while(q--){
int x=read(),y=update_range(fa[x],a[x]);
int now=fa[idx[y]!=0?idx[y]:x];
a[x]^=1;
if(now){
int res=SEG::query(1,1,n,dfn[now]);
if(res!=2) a[now]=res;
if(res==a[x]) SEG::update_p(1,1,n,dfn[now],2);
else SEG::update_p(1,1,n,dfn[now],a[now]);
}
printf("%d\n",get_p(1));
}
return 0;
}

「SHOI2014」三叉神经树的更多相关文章

  1. 「SHOI2014」三叉神经树 解题报告

    「SHOI2014」三叉神经树 膜拜神仙思路 我们想做一个类似于动态dp的东西,首先得确保我们的运算有一个交换律,这样我们可以把一长串的运算转换成一块一块的放到矩阵上之类的东西,然后拿数据结构维护. ...

  2. 【LOJ】#2187. 「SHOI2014」三叉神经树

    题解 可以发现每次修改的是这个点往上一条连续的链,如果我要把1改成0,需要满足这一段往上的一部分都有两个1 如果我要把0改成1,需要满足这一段往上的部分有两个0 对于每个点记录1的个数,发现我们只会把 ...

  3. Loj #2192. 「SHOI2014」概率充电器

    Loj #2192. 「SHOI2014」概率充电器 题目描述 著名的电子产品品牌 SHOI 刚刚发布了引领世界潮流的下一代电子产品--概率充电器: 「采用全新纳米级加工技术,实现元件与导线能否通电完 ...

  4. 「模板」 线段树——区间乘 && 区间加 && 区间求和

    「模板」 线段树--区间乘 && 区间加 && 区间求和 原来的代码太恶心了,重贴一遍. #include <cstdio> int n,m; long l ...

  5. [LOJ 2190] 「SHOI2014」信号增幅仪

    [LOJ 2190] 「SHOI2014」信号增幅仪 链接 链接 题解 坐标系直到 \(x\) 轴与椭圆长轴平行 点的坐标变换用旋转公式就可以了 因为是椭圆,所以所有点横坐标除以 \(p\) 然后最小 ...

  6. Loj #2570. 「ZJOI2017」线段树

    Loj #2570. 「ZJOI2017」线段树 题目描述 线段树是九条可怜很喜欢的一个数据结构,它拥有着简单的结构.优秀的复杂度与强大的功能,因此可怜曾经花了很长时间研究线段树的一些性质. 最近可怜 ...

  7. 【LOJ】#3043. 「ZJOI2019」线段树

    LOJ#3043. 「ZJOI2019」线段树 计数转期望的一道好题-- 每个点设两个变量\(p,q\)表示这个点有\(p\)的概率有标记,有\(q\)的概率到祖先的路径上有个标记 被覆盖的点$0.5 ...

  8. 【LOJ】#2983. 「WC2019」数树

    LOJ2983. 「WC2019」数树 task0 有\(i\)条边一样答案就是\(y^{n - i}\) task1 这里有个避免容斥的方法,如果有\(i\)条边重复我们要算的是\(y^{n - i ...

  9. 「ZJOI2019」线段树 解题报告

    「ZJOI2019」线段树 听说有人喷这个题简单,然后我就跑去做,然后自闭感++,rp++(雾) 理性分析一波,可以发现最后形成的\(2^k\)个线段树,对应的操作的一个子集,按时间顺序作用到这颗线段 ...

随机推荐

  1. JS实现动态显示时间(最简单方法)

    使用JS实现动态显示时间 最简单实现方法 直接在网页适当的位置中插入如下js代码,(id="datetime") 不可省略. <div id="datetime&q ...

  2. 07 Sublime Text3常用快捷键

    通用常用类(General) ↑↓←→:上下左右移动光标,注意不是不是 KJHL ! Alt:调出菜单 Ctrl + Shift + P:调出命令板(Command Palette) Ctrl + ` ...

  3. 编程体系结构(06):Java面向对象

    本文源码:GitHub·点这里 || GitEE·点这里 一.基础概念 1.面向对象概念 面向对象编程的主要思想是把构成问题的各个事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙一 ...

  4. oracle 11g linux 导入中文字符乱码问题解决

    1. 涉及的字符集 这个可以分成三块,数据库服务器字符集(server).实例字符集(instance), 会话字符集(session) 2. 乱码的原因 session 的字符集和 server 的 ...

  5. MySQL - 常用三种数据库存储引擎

    数据库存储引擎:是数据库底层软件组织,数据库管理系统(DBMS)使用数据引擎进行创建.查询.更新和删除数据.不同的存储引擎提供不同的存储机制.索引技巧.锁定水平等功能,使用不同的存储引擎,还可以获得特 ...

  6. 从面试角度学完 Kafka

    Kafka 是一个优秀的分布式消息中间件,许多系统中都会使用到 Kafka 来做消息通信.对分布式消息系统的了解和使用几乎成为一个后台开发人员必备的技能.今天码哥字节就从常见的 Kafka 面试题入手 ...

  7. [Leetcode题解]2. 两数相加-链表遍历和重构

    1. 审题leetcode 02 add-two-numbers​ 我们先看一下题目,如下  : 链表的从前往后为数字的低位到高位,模拟加法手算过程,从前往后遍历即可, 注意每个数字0-9,进位要处理 ...

  8. 第六章 SSH远程服务介绍

    一.相关介绍 1.简介SSH是一个安全协议,在进行数据传输时,会对数据包进行加密处理,加密后在进行数据传输.确保了数据传输安全.那SSH服务主要功能有哪些呢? 1)提供远程连接的服务 linux远程连 ...

  9. java: 非法字符: '\ufeff'

    错误问题记录: Error:(1, 1) java: 非法字符: '\ufeff' Error:(1, 1) 错误: 需要class, interface或enum报错 问题发生时因为编码问题导致,如 ...

  10. 使用ModelForm校验数据唯一性

    在设计模型类的时候,将指定字段设置unique=true属性,可以保证该字段在数据库中的唯一性. 使用ModelForm可以将指定模型类快速生成表单元素.在提交数据后,使用is_valid()校验时, ...