Luogu4332

LOJ2187

题解

代码-Tea

题意 : 每个点有三个儿子 , 给定叶节点的权值\(0\)或\(1\)且支持修改 , 非叶子节点的权值为当有\(>=2\)个儿子的权值为\(1\)的时候取\(1\) , 每次修改后求根节点输出

定义 : 权值可以取\(0,1,2,3\) ; 输出为\(0\)或\(1\)且权值\(>=2\)时输出为\(1\) .

限制 : 修改的都是叶子节点

考虑如果把输出\(0\)改成\(1\) , 则找到祖先中最深的权值不为\(1\)的点 , 这条链上的输出会改变 , 这里用\(LCT\)维护

如果把输出\(1\)改成\(0\) , 则找到祖先中最深的权值不为 \(2\) 的点 , 这条链上的输出会改变为\(0\)

这样有点麻烦 , 来一个转化 , 叶节点权值取值范围由\(0,1\)变为\(1,2\) ; 统一变成了权值\(2\)会对父亲产生贡献 , 权值\(1\)对父亲没有贡献

定义\(not1[x]=true\)的意义是\(x\)的子树中存在 权值不为\(1\)的点 , \(not2[]\)类似 .

只要写了\(pushdown\) , \(LCT\)也能区间打标记 .

打标记时先潦草地搞一下有类似\(not2[x]=not1[x],not1[x]=0\)的操作 , 之后在\(pushup\)的时候会有简单而正确的更新 : \(not1[x]=not1[ls]\ |\ not1[rs]\ |\ (val[x]!=1);\)

一定要想清楚所有状况才能说明这是对的 : 如果把\(0\)改成\(1\) , 可能碰到一个权值为\(0\)的点变成\(1\) , 或者\(2\)变成\(3\)而停止 ;

如果把\(1\)变成\(0\) , 可能碰到一个权值为\(1\)的点变成\(0\) , 或者\(3\)变成\(2\)而停止 .

所以\(pushup\)是\(LCT\)的关键 , 也是所有数据结构的关键 , 尽管在一般线段树题目中看不出来 ,

但在复杂一点的比如P4198楼房重建[HNOI/AHOI2018]转盘便显得非常重要了 .

类似地 , [NOIP2018]保卫王国中从下往上转移使得矩阵乘的更新顺序改变 , 说明\(pushup\)操作真的非常重要.

最简单的操作却能更新很难更新的情况,从而维护正确性

这道题还用到了一个点 , \(access\)可以使无关的点断开 , 而不直接\(fa[x]=0,ch[y][_]=0\)是因为\(access\)包括了\(pushup\)的更新

两个端点找出来以后就可以用上面说的修改和打标记了

注释更新于\(19.3.29\)

#include<bits/stdc++.h>
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define Debug(x) cout<<#x<<"="<<x<<endl
using namespace std;
typedef long long LL;
const int INF=1e9+7;
inline LL read(){
register LL x=0,f=1;register char c=getchar();
while(c<48||c>57){if(c=='-')f=-1;c=getchar();}
while(c>=48&&c<=57)x=(x<<3)+(x<<1)+(c&15),c=getchar();
return f*x;
} const int MAXN=1.5e6+5; int fa[MAXN],to[MAXN][3];
int n,m; namespace LCT{
int par[MAXN],ch[MAXN][2],val[MAXN],tag[MAXN];
bool not1[MAXN],not2[MAXN];//定义not1[x]=true的意义是x的子树中存在权值不为1的点
int st[MAXN],top;
#define ls (ch[rt][0])
#define rs (ch[rt][1])
inline bool chk(int x){return ch[par[x]][1]==x;}
inline bool isnotroot(int x){return ch[par[x]][0]==x||ch[par[x]][1]==x;}
inline void pushup(int rt){
not1[rt]=not1[ls]|not1[rs]|(val[rt]!=1);//最简单的维护却可以更新很难更新的情况,从而维护正确性. pushup是LCT的关键
not2[rt]=not2[ls]|not2[rs]|(val[rt]!=2);
}
inline void modify(int x,int y){
val[x]+=y,tag[x]+=y;//区间树上打标记
if(y>0) not2[x]=not1[x],not1[x]=0;//对整段val[]=1的区间加1,val[i]=0变1的误判会在pushup()里改正,然后就没有别的情况了
else not1[x]=not2[x],not2[x]=0;//这里的修改只是简单的维护
}
inline void pushdown(int rt){
if(!tag[rt]) return;
if(ls) modify(ls,tag[rt]);
if(rs) modify(rs,tag[rt]);
tag[rt]=0;
}
inline void rotate(int x){
int y=par[x],z=par[y],k=chk(x),w=ch[x][k^1];
ch[y][k]=w,par[w]=y;
if(isnotroot(y)) ch[z][chk(y)]=x; par[x]=z;
ch[x][k^1]=y,par[y]=x;
pushup(y);pushup(x);
}
inline void splay(int x){
int y=x,top=0;
while(1){
st[++top]=y;
if(isnotroot(y)) y=par[y];
else break;
}
while(top) pushdown(st[top--]);
while(isnotroot(x)){
int y=par[x];
if(isnotroot(y)){
if(chk(x)==chk(y)) rotate(y);
else rotate(x);
}
rotate(x);
}
}
inline void access(int x){
for(int y=0;x;x=par[y=x])
splay(x),ch[x][1]=y,pushup(x);
}
inline int findnot1(int rt){
pushdown(rt);
if(!not1[rt]) return 0;
if(not1[rs]) return findnot1(rs);//找到深度最大的不是1的点,即能够影响到的最远的点
if(val[rt]!=1) return rt;
return findnot1(ls);
}
inline int findnot2(int rt){
pushdown(rt);
if(!not2[rt]) return 0;
if(not2[rs]) return findnot2(rs);
if(val[rt]!=2) return rt;
return findnot2(ls);
}
#undef ls
#undef rs
}using namespace LCT; inline void dfs(int u){
if(u>n) return;//因为初始数据一定是合法的,所以没必要往更深的地方走
for(int i=0;i<=2;i++){
int v=to[u][i];
dfs(v);
val[u]+=(val[v]>>1);//只有权值>=1的才有意义,先简单搞一下贡献
}
// val[u]>>=1;
} int main(){
// freopen("asd.in","r",stdin);
n=read();
for(int i=1;i<=n;i++)
for(int j=0;j<=2;j++){
int x=read();to[i][j]=x;
par[x]=i,fa[x]=i;
}
for(int i=n+1;i<=3*n+1;i++) val[i]=read()+1;
//根据定义,val[u]应该+=(val[v]/2).(对于底层节点(其实适应于所有节点))考虑到转换的方便用1和2表示.
//1就代表对上一层没贡献,2就代表有贡献.
dfs(1);//算出初始的答案
m=read();
for(int i=1;i<=m;i++){
int x=read();
//实际上这里是一种卡常,如果split(x,y)就要makeroot极慢,所以改为这样
access(x);splay(x);
if(val[x]>1){
int y=findnot2(ch[x][0]);
// Debug(fa[y]); fa[y]是原树中的父亲,不是splay中的.
access(fa[y]);//这里的作用是把无关的点断开,要access的理由是包括了pushup操作.
splay(x);//access中使得根变了,所以要重新splay.
modify(x,-1);
}
else{
int y=findnot1(ch[x][0]);
access(fa[y]);
splay(x);
modify(x,1);
}
splay(1);//要把1节点旋上来才能更新答案
printf("%d\n",val[1]>>1);
}
}

3.2原始注释

P4332 [SHOI2014]三叉神经树(LCT)的更多相关文章

  1. 洛谷P4332 [SHOI2014]三叉神经树(LCT,树剖,二分查找,拓扑排序)

    洛谷题目传送门 你谷无题解于是来补一发 随便百度题解,发现了不少诸如树剖\(log^3\)LCT\(log^2\)的可怕描述...... 于是来想想怎么利用题目的性质,把复杂度降下来. 首先,每个点的 ...

  2. 洛谷P4332 [SHOI2014]三叉神经树(LCT)

    传送门 FlashHu大佬太强啦%%% 首先,我们可以根据每一个点的权值为$1$的儿子的个数把每个点记为$0~3$,表示这一个点的点权 先考虑一下暴力的过程,假设从$0$变为$1$,先更改一个叶子结点 ...

  3. P4332 [SHOI2014]三叉神经树

    \(\color{#0066ff}{ 题目描述 }\) 计算神经学作为新兴的交叉学科近些年来一直是学术界的热点.一种叫做SHOI 的神经组织因为其和近日发现的化合物 SHTSC 的密切联系引起了人们的 ...

  4. BZOJ 3553: [Shoi2014]三叉神经树 LCT

    犯傻了,想到了如果是 0->1 的话就找最深的非 1 编号,是 1 -> 0 的话就找最深的非 0 编号. 但是没有想到这个东西可以直接维护. 假设不考虑叶子节点,那么如果当前点的值是 1 ...

  5. [BZOJ 3553][SHOI2014]三叉神经树

    传送门(下面也有题面) 题目大意: 一颗有根树,每个非叶子节点都有三个子节点,每个节点的权为0/1. 每个节点的权 取决于其所有子节点中 哪种权出现的次数更多. 有若干次询问,每次询问修改一个叶子节点 ...

  6. 洛谷4322 SHOI2014 三叉神经树(LCT+思维)

    好久之前做的题了QWQ 现在来补一发博客 一道神仙题啊..qwq 首先,我们可以看出来,我们如果对于每个点维护一个\(val\),表示他的直系儿子中有几个表现为1的. 那么\(val[x]>&g ...

  7. [SHOI2014]三叉神经树

    题目描述 计算神经学作为新兴的交叉学科近些年来一直是学术界的热点.一种叫做SHOI 的神经组织因为其和近日发现的化合物 SHTSC 的密切联系引起了人们的极大关注. SHOI 组织由若干个 SHOI ...

  8. BZOJ3553 : [Shoi2014]三叉神经树

    设val[i]为i连出去的树突中输出值为0的个数 如果val[x]<=1,输出值为1,否则输出值为0 修改x就相当于val[f[i]]++或者val[f[i]]-- 用Link-cut Tree ...

  9. 3553: [Shoi2014]三叉神经树(树链剖分)

    这道题特别恶心,首先我们可以发现更改的就是出现连续的一或二,那么就用线段树+树链剖分找到这个范围 想到是不难想,就是打起来恶心罢了= = CODE: #include<cstdio> #i ...

随机推荐

  1. DAY5-常用模块

    一 time与datetime模块 二.random模块 三.os模块 四.sys模块 五 shutil模块 六 json&pickle模块 七 shelve模块 八 xml模块 九 conf ...

  2. AOP基础-JDK动态代理

    动态代理技术就是用来产生一个目标对象的代理对象的,代理对象应与目标对象(被代理的对象)有相同的方法,实现对目标对象访问的拦截,并增强目标对象的一些功能,而不需要目标对象去做任何的更改,使得目标对象有更 ...

  3. Markdown编辑器及图床推荐

    Typora和自动图床工具 Typora 地址 ,极致简洁,界面很漂亮,最重要的是所见即所得 百度云搬运 密码:xi01 自动图床工具 需要七牛云做图床,感谢作者,详见博客 使用方法,只需两步即可完成 ...

  4. 一些API的用法

    //1.init初始化 NSString * str1 = [[NSString alloc] init]; NSLog(@"str1 = %@",str1); //2.initW ...

  5. redis的特性

  6. Docker的Gitlab镜像的使用

    Gitlab是一款非常强大的开源源码管理系统.它支持基于Git的源码管理.代码评审.issue跟踪.活动管理.wiki页面,持续集成和测试等功能.基于Gitlab,用户可以自己搭建一套类似Github ...

  7. CSS技巧: CSS隐藏文字的方法(CSS text-indent: -9999px;)

    建站过过程中朋友喜欢把网站名称用H1表示,但从美观考虑,要用logo图片来代替h1,这时需要隐藏h1内的这段文字,但又不能对搜索引擎不友好,否则就失去了定义h1标签的意义. 在CSS中如何以图代字,找 ...

  8. solr :term 查询, phrase查询, boolean 查询

    搜索总体有:term 查询, phrase查询, boolean 查询 1. SOLR搜索覆盖度和准确度保证的三个搜索方式: 保证准确率: AND: Search for two different ...

  9. 托盘在XP下不能显示tooltip,在Vista和Windows7下正常

    转自:http://blog.csdn.net/debehe/article/details/4294053 奇怪的问题,想了很多可能的理由,最终的答案竟然是一开始就被我否认了的一种可能!! 问题现象 ...

  10. c类库,委托,var ,运算符 is 和 as 。

    类库(Class Library)  格式   .dll  文件 类库   就是类的仓库 c#代码被编译过以后的文件,不可阅读,不可修改,只能调用. 类库是一个综合性的面向对象的可重用类型集合,这些类 ...