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. CALayer绘图

    一.CALayer绘图方式 Layer绘图有两种方法,不管使用哪种方法绘制完必须调用图层的setNeedDisplay方法(注意是图层的方法,不是UIView的方法,UIView的setNeedDis ...

  2. xcode减小静态库的大小(转)

    减小静态库的大小 编译生成的.a文件太大,但又没有冗余的文件可以删除已减少体积,找了很久才找到解决办法,如下: Build Settings-->Generate Debug Symbols 将 ...

  3. js在浏览器下的区别小结(部分)

    1.初始化数组: document.write([1,2,3,].length); IE:4//把数组中最后一个逗号后面的当做了undefined元素 FF.Opera.Safari:3 2.join ...

  4. kernel下制作动态logo

    kernel下制作动态logo 在uboot中实现logo的好处是反映速度快. 在kernel中实现logo的好处是,不管是android还是什么其他平台,logo显示无需考虑上层平台. 参照三星平台 ...

  5. 未找到与约束contractname Microsoft.VisualStudio.Utilities.IContentTypeRegistryService...匹配的导出

    ***以下操作后一定要重启VS,重启VS,重启VS重要的事情说三遍*** //************************************************************* ...

  6. sqlplus--sqlldr基础运用

    a.ctl load data                                         infile *                                     ...

  7. 在页面所有元素加载完成之后执行某个js函数

    在页面所有元素加载完成之后执行某个js函数 http://lgscofield.iteye.com/blog/1884352

  8. ROS Learning-024 (提高篇-002) rviz的安装和使用

    ROS 提高篇-002 - rviz 的安装和使用 我使用的虚拟机软件:VMware Workstation 11 使用的Ubuntu系统:Ubuntu 14.04.4 LTS ROS 版本:ROS ...

  9. 深、浅copy

    深.浅copy多用于列表 浅copy:第一层中不变的数据是独立的,可变类型元素指向同一块内存地址 l1 = [1,2,3,["a","b"]] l2 = l1. ...

  10. static的功能

    static : 翻译成中文是静态的意思.  使用内部函数的好处是:不同的人编写不同的函数时,不用担心自己定义的函数,是否会与其它文件中的函数同名,因为同名也没有关系.   在C语言中,static的 ...