[SHOI2014]三叉神经树
Description:
计算神经学作为新兴的交叉学科近些年来一直是学术界的热点。一种叫做SHOI 的神经组织因为其和近日发现的化合物 SHTSC 的密切联系引起了人们的极大关注。
SHOI 组织由若干个 SHOI 细胞构成,SHOI 细胞之间形成严密的树形结构。每个 SHOI 细胞都有且只有一个输出端,被称为轴突,除了一个特殊的、被称为根细胞的 SHOI 细胞的输出作为整个组织的输出以外,其余细胞的轴突均连向其上级 SHOI 细胞;并且有且只有三个接收端,被称为树突,从其下级细胞或者其它神经组织那里接收信息。SHOI 细胞的信号机制较为简单,仅有 0和 1两种。每个 SHOI 细胞根据三个输入端中 0和 1信号的多寡输出较多的那一种。
现在给出了一段 SHOI 组织的信息,以及外部神经组织的输入变化情况。请你模拟 SHOI 组织的输出结果。
Description:
输入的第一行包含一个整数 n。表示 SHOI 组织的总细胞个数。SHOI 细胞由 1~ n 编号,编号为 1 的是根细胞。
从第二行开始的 n行,每行三个整数 \(x_1, x_2, x_3\)
,分别表示编号为 1~ n 的 SHOI 细胞的树突连接。$1 < x_i \leq ≤n \(
表示连向编号为\) x_i$
的细胞的轴突, n < x_i \leq 3n+1
≤3n+1 表示连向编号为 x_i
的外界输入。输入数据保证给出的 SHOI 组织是合法的,且所有的 x_i
两两不同。
接下来一行包含 2n+1 个 0/1的整数,表示初始时的外界输入。
第 n+3行有一个整数 q,表示总操作数。
之后 q 行每行一个整数 x,表示编号为 x 的外界输入发生了变化。
Solution:
很神仙的一道题,思路秒出,但是细节真的很多,很久才想清楚
首先考虑每次修改只会改变状态为1/2的点
考虑用LCT维护#splay#的点子树中有没有不为1/2的点
举个例子,现在把一个叶节点从0修改成1
每次拉出一条链后把叶节点旋到根,并找到#原树中深度最大#且不为1的点
显然从叶节点到这个点的所有的点的状态都要+1,而对其他点没有影响
于是打个标记就行了,1修改成0同理
至于询问,直接输出状态就行
代码还是有很多细节的
#include<bits/stdc++.h>
using namespace std;
const int mxn=2e6+5;
int n,q,s;
int a[mxn],son[mxn][3],st[mxn], val[mxn], tag[mxn], fa[mxn],f[mxn],no1[mxn], no2[mxn];
inline void chkmax(int &x,int y) {if(x<y) x=y;}
inline void chkmin(int &x,int y) {if(x>y) x=y;}
namespace lct {
int ch[mxn][2];
int isnotrt(int x) {
return ch[fa[x]][0]==x||ch[fa[x]][1]==x;
}
void update(int x,int z) {
val[x]+=z; tag[x]+=z;
if(z>0) no2[x]=no1[x],no1[x]=0;
else no1[x]=no2[x],no2[x]=0; //很方便的继承,考虑这样为什么不会错,因为这条路径上不是1就是2
}
void push_up(int x) {
no1[x]=no1[ch[x][0]]|no1[ch[x][1]]|(val[x]!=1);
no2[x]=no2[ch[x][0]]|no2[ch[x][1]]|(val[x]!=2);
}
void push_down(int x) {
if(tag[x]) {
if(ch[x][0]) update(ch[x][0],tag[x]);
if(ch[x][1]) update(ch[x][1],tag[x]);
tag[x]=0;
}
}
void rotate(int x) {
int y=fa[x],z=fa[y],tp=ch[y][1]==x;
if(isnotrt(y)) ch[z][ch[z][1]==y]=x; fa[x]=z;
ch[y][tp]=ch[x][tp^1]; fa[ch[x][tp^1]]=y;
ch[x][tp^1]=y; fa[y]=x;
push_up(y); push_up(x);
}
void splay(int x) {
int u=x; s=0; st[++s]=x;
while(isnotrt(u)) st[++s]=u=fa[u];
while(s) push_down(st[s]),--s;
while(isnotrt(x)) {
int y=fa[x],z=fa[y];
if(isnotrt(y))
(ch[y][1]==x)^(ch[z][1]==y)?rotate(x):rotate(y);
rotate(x);
}
}
void access(int x) {
for(int y=0;x;x=fa[y=x])
splay(x),ch[x][1]=y,push_up(x);
}
int find1(int x) {
push_down(x);
if(!no1[x]) return 0;
if(no1[ch[x][1]]) return find1(ch[x][1]);
if(val[x]!=1) return x; //找点
return find1(ch[x][0]);
}
int find2(int x) {
push_down(x);
if(!no2[x]) return 0;
if(no2[ch[x][1]]) return find2(ch[x][1]);
if(val[x]!=2) return x;
return find2(ch[x][0]);
}
}
using namespace lct;
void dfs(int u)
{
if(u>n) return ;
for(int i=0;i<3;++i) {
dfs(son[u][i]);
val[u]+=(val[son[u][i]]>1); //赋初值
}
}
int main()
{
scanf("%d",&n); int x;
for(int i=1;i<=n;++i) {
for(int j=0;j<3;++j) {
scanf("%d",&son[i][j]);
fa[son[i][j]]=f[son[i][j]]=i;
}
}
for(int i=1;i<=2*n+1;++i)
scanf("%d",&val[n+i]),++val[n+i];
dfs(1); scanf("%d",&q);
for(int i=1;i<=q;++i) {
scanf("%d",&x); int pos;
access(x); splay(x);
if(val[x]>1) {
pos=find2(ch[x][0]); access(f[pos]);
splay(x); update(x,-1);
}
else {
pos=find1(ch[x][0]); access(f[pos]);
splay(x); update(x,1);
}
splay(1);
printf("%d\n",val[1]>1);
}
return 0;
}