[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;
}
posted @ 2019-02-28 22:36  cloud_9  阅读(213)  评论(0编辑  收藏  举报