Luogu P4332 [SHOI2014]三叉神经树 (LCT)

题目描述

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

SHOI 组织由若干个 SHOI 细胞构成,SHOI 细胞之间形成严密的树形结构。每个 SHOI 细胞都有且只有一个输出端,被称为轴突,除了一个特殊的、被称为根细胞的 SHOI 细胞的输出作为整个组织的输出以外,其余细胞的轴突均连向其上级 SHOI 细胞;并且有且只有三个接收端,被称为树突,从其下级细胞或者其它神经组织那里接收信息。SHOI 细胞的信号机制较为简单,仅有 00和 11两种。每个 SHOI 细胞根据三个输入端中 00和 11信号的多寡输出较多的那一种。

现在给出了一段 SHOI 组织的信息,以及外部神经组织的输入变化情况。请你模拟 SHOI 组织的输出结果。

 

思路:

如果能改变一个细胞输出, 那么这个细胞的值一定是 1, 或者 2, 

所以我们要维护一个从底部开始第一个不是1 或者2 的位置,然后修改就可以了.

每次修改都是修改连续的 1 ,或者连续的 0.

 


#include<bits/stdc++.h>
using namespace std;
const int N = 5e5+100;
int tag[N<<2],val[N<<2],f[N<<2],ch[N<<2][2],hep[N<<2],rev[N<<2],dep1[N<<2],dep2[N<<2];
int n,m,du[N];
inline int isroot(int x) {
    return ch[f[x]][0] != x && ch[f[x]][1] != x;
}
void pushup(int x){  //优先找右子树. 
    dep1[x] = dep1[ch[x][1]];
    if (!dep1[x] && val[x] != 1) dep1[x] = x;
    if (!dep1[x]) dep1[x] = dep1[ch[x][0]];
    dep2[x] = dep2[ch[x][1]];
    if (!dep2[x] && val[x] != 2) dep2[x] = x;
    if (!dep2[x]) dep2[x] = dep2[ch[x][0]];
}
void solve(int x, int d){
    if (!x) return;
    val[x] ^= 3;
    swap(dep2[x],dep1[x]);
    tag[x] += d;
}
void pushdown(int x){
    if (!tag[x]) return ;
    solve(ch[x][0],tag[x]);
    solve(ch[x][1], tag[x]);
    tag[x] = 0;
}
inline void rotate(int x){
    int y = f[x], z = f[y], k = ch[y][1] == x, v = ch[x][!k];
    if (!isroot(y)) ch[z][ch[z][1] == y] = x;//Attention if()
    ch[x][!k] = y; ch[y][k] = v;
    if (v) f[v] = y; 
    f[y] = x, f[x] = z;
    pushup(y); pushup(x);
}
inline void splay(int x){
    int y = x, top = 0; hep[++top] = y;
    while(!isroot(y)) hep[++top] = y = f[y];  //这里个splay 不同之处,用一个栈存下来节点.
    while(top) pushdown(hep[top--]);    //然后一个一个的从上往下pushdown .
    while(!isroot(x)){
        y = f[x]; top = f[y];
        if (!isroot(y))
            rotate((ch[y][0] == y) ^ (ch[top][0] == y)?x:y);
        rotate(x);
    }
    pushup(x);
}

inline void access(int x){ 
    for(int y = 0; x; x = f[y = x])
        splay(x), ch[x][1] = y, pushup(x);
}




void init(){
    int x,y,z;
    queue<int>q;
    scanf("%d",&n);
    for (int i = 1; i <= n; ++i){
        scanf("%d%d%d",&x,&y,&z);
        f[x] = f[y] = f[z] = i;
        du[i] = 3;
    }
    // val[i] 代表每个点的权值,然后 val[i] >> 1 就是要输出的值,
    for (int i =  n+1; i <= 3*n+1; ++i){
        scanf("%d",&x); val[i] = x * 2; q.push(i);  //这里为了方便运算,所以叶子节点的值也都 *2.
    }
    while(!q.empty()){  //这里是确定每个点的val值.
        int u = q.front(); q.pop();
        int v = f[u];
        if (u <= n) pushup(u);
        val[v] += val[u]/2;
        du[v]--;
        if (du[v] == 0) q.push(v);
    }
}
int main(){
    int x;
    init();
    int ans = val[1] >> 1;
    scanf("%d",&m);
    for (int i = 0; i < m; ++i){
        scanf("%d",&x);
        val[x] ^= 2; //一开始 val 的值就 * 2 了, 所以val 的值只有两种可能, 0 或者 2. 
        int k = val[x] - 1;  // k  的值是确定 +1 还是 -1. 
        x = f[x];
        access(x); splay(x);  //首先 从 x 的父亲拉一条链出来.
        int now;
        if (k == -1) now = dep2[x]; else now = dep1[x];  //要更新的点在 x 和 now之间.
        if (!now) {
            solve(x,k); pushup(x);  //代表更新整条链.
            ans ^= 1;
        } else{  // 找到要更新的区间.splay一下,然后更新.
            splay(now);
            solve(ch[now][1],k);  //更新右子树.
            pushup(ch[now][1]);  //更新完之后,向上合并.
            val[now] += k;  // 这个节点单独更新. 这个节点的值不是 1 或者 2.
            pushup(now);  // 向上合并.
        }
        printf("%d\n",ans);
    }

    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值