[SHOI2014]三叉神经树

题目描述

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

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

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

题解

身体啊。

先考虑每一次修改能影响到的范围,这个比较简单,只能是这个点到根的路径上的所有点。

再次进一步的考虑,发现能够影响到的范围是一段连续的区间。

考虑为什么题目中给的是一颗每个点有三个儿子的树?如果一个节点时是1,那么它的1的儿子树时2或3,也就是d>>1=1,所以我们判断一个节点是否是被激活的就可以用度数/2来判断。

然后考虑某个叶子从1->0的时候,那么有修改的就是从这个叶子往上连续的一段2,和第一个不是2的点。

反之就是往上连续一段1和第一个不是1的点。

这是一个非常重要的性质,有了这个性质之后就可以二分最靠下的不是1/2的点,用&*%*#数据结构维护一下。

题解给的LCT做法非常喵。

我们不用二分,直接在LCT每个节点维护当前链中深度最深的不是1/2的点。

但是有个问题,这是一颗有根树,我们不能makeroot。

那么如何提取一条链?

假设我们的叶子为x,链顶为y,那么access&&splay(x)之后,当前的splay就是从叶子到根的一条链。

然后再splay(y)此时y的右子树就是我们要的链了。

我的写法如果第一个不是1/2的点是根的话要特判,为啥网上的题解都不同特判啊。。

代码

#include<iostream>
#include<cstdio>
#include<queue>
#define N 500009
#define ls ch[x][0]
#define rs ch[x][1]
using namespace std;
queue<int>q;
int ch[N][2],fa[N],v[N*3],rev[N],n1[N],n2[N],n,f[N*3],du[N];
typedef long long ll;
inline ll rd(){
    ll x=0;char c=getchar();bool f=0;
    while(!isdigit(c)){if(c==‘-‘)f=1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return f?-x:x;
}
inline bool ge(int x){return ch[fa[x]][1]==x;}
inline bool isroot(int x){return ch[fa[x]][1]!=x&&ch[fa[x]][0]!=x;}
inline void pushup(int x){
    n1[x]=n1[rs];if(!n1[x])n1[x]=(v[x]!=1)?x:n1[ls];
    n2[x]=n2[rs];if(!n2[x])n2[x]=(v[x]!=2)?x:n2[ls];
}
inline void rotate(int x){
    int y=fa[x],o=ge(x);
    ch[y][o]=ch[x][o^1];fa[ch[y][o]]=y;
    if(!isroot(y))ch[fa[y]][ge(y)]=x;fa[x]=fa[y];
    fa[y]=x;ch[x][o^1]=y;pushup(y);pushup(x);
}
inline void pushdown(int x,int tag){
    if(!x)return;
    v[x]^=3;swap(n1[x],n2[x]);rev[x]+=tag;
}
void _pushdown(int x){
    if(!isroot(x))_pushdown(fa[x]);
    if(rev[x])pushdown(ls,rev[x]),pushdown(rs,rev[x]),rev[x]=0;
}
inline void splay(int x){
    _pushdown(x);
    while(!isroot(x)){
        int y=fa[x];
        if(isroot(y))rotate(x);
        else rotate(ge(x)==ge(y)?y:x),rotate(x);
    }
}
inline void access(int x){
    for(int y=0;x;y=x,x=fa[x])splay(x),ch[x][1]=y,pushup(x);
}
int main(){
    n=rd();int x,y,z;
    for(int i=1;i<=n;++i)f[i]=i;
    for(int i=1;i<=n;++i){
        x=rd();y=rd();z=rd();
        if(x>n)f[x]=i;else fa[x]=i,du[i]++;
        if(y>n)f[y]=i;else fa[y]=i,du[i]++;
        if(z>n)f[z]=i;else fa[z]=i,du[i]++;
    }
    for(int i=n+1;i<=3*n+1;++i)v[i]=rd(),v[f[i]]+=v[i];
    for(int i=1;i<=n;++i)if(!du[i])q.push(i);
    while(!q.empty()){
        int u=q.front();q.pop();
        v[fa[u]]+=v[u]>>1;
        if(!--du[fa[u]])q.push(fa[u]);
    }
    int ans=0,qu;
    ans=v[1]>>1;
    qu=rd();
    while(qu--){
        x=rd();v[x]^=1;int ta=v[x]?1:-1;x=f[x];
    //    cout<<x<<" "<<n1[x]<<" "<<n2[x]<<endl;
        access(x);splay(x);
        if(ta==1&&n1[x]){
            x=n1[x];//cout<<"1"<<" haha "<<x<<endl;
            splay(x);
            pushdown(rs,ta);v[x]+=ta;pushup(x);
            if(x==1)ans=v[1]>>1;
        }
        else if(ta==-1&&n2[x]){
            x=n2[x];//cout<<"2"<<" haha "<<x<<endl;
            splay(x);
            pushdown(rs,ta);v[x]+=ta;pushup(x);
            if(x==1)ans=v[1]>>1;
        }
        else{
           pushdown(x,ta);pushup(x);ans^=1;
        }
        if(ans)puts("1");else puts("0");
    }
    return 0;
} 

原文地址:https://www.cnblogs.com/ZH-comld/p/10294165.html

时间: 2024-10-16 04:05:18

[SHOI2014]三叉神经树的相关文章

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

传送门(下面也有题面) 题目大意: 一颗有根树,每个非叶子节点都有三个子节点,每个节点的权为0/1. 每个节点的权 取决于其所有子节点中 哪种权出现的次数更多. 有若干次询问,每次询问修改一个叶子节点的权,然后输出修改后根节点的权. 给出叶子节点初始值. 解法:树链剖分+线段树 叶子节点和非叶子节点的性质不同,为了省却麻烦,我们把叶子节点去掉, 每次修改叶子节点就直接修改其父亲.以下的“叶子节点”均指处理后的树的叶子节点. 如果用num[]记录每个节点的权为1的子节点个数, 那么当num[i]>

BZOJ3553 [Shoi2014]三叉神经树

容易想到树链剖分来维护 一条链上维护儿子中是1的个数为1的点的最长值和儿子是1的个数为2的点的最长值 于是每次修改的时候就二分查询会更新到哪里,再直接链修改就好了 单次查询复杂度$O(logn^2)$,单次修改复杂度为$O(logn)$ 注意如果动态开点太多会导致MLE,最后解决办法是在每个线段树节点上增加了一个res变量表示返回值 1 /************************************************************** 2 Problem: 3553

【BZOJ-3553】三叉神经树 树链剖分

3553: [Shoi2014]三叉神经树 Time Limit: 160 Sec  Memory Limit: 256 MBSubmit: 347  Solved: 112[Submit][Status][Discuss] Description 计算神经学作为新兴的交叉学科近些年来一直是学术界的热点.一种叫做SHOI 的神经组织因为其和近日发现的化合物 SHTSC 的密切联系引起了人们的极大关注.SHOI 组织由若干个 SHOI 细胞构成,SHOI 细胞之间形成严密的树形结构.每个 SHOI

数据结构(下)

1.树套树 (1)树状数组套树状数组 前置: 树状数组如何支持区间加以及区间查询 维护一个差分数组,用来求一个位置的值,我们只需要把前缀和看作是一个矩形, 减去两数插值产生的贡献即可 1.P4514 上帝造题的七分钟 题意:支持矩形加某个数和矩形查询值. 考虑将每个点差分,当我们要将\((a,b),(x,y)\)范围内的矩阵加1时,其实就是: \(A(a,b)+1\) \(A(x,b+1)-1\) \(A(a,y+1)-1\) \(A(x+1,y+1)+1\) 于是乎,前缀和: \[\sum_{

[BZOJ 3637]Query on a tree VI

偶然看见了这题,觉得自己 QTREE.COT 什么的都没有刷过的真是弱爆了…… 一道思路很巧妙的题,终于是在约大爷的耐心教导下会了,真是太感谢约大爷了. 这题显然是树链剖分,但是链上维护的东西很恶心.其核心思想是找到一个相连的最浅同色节点,那么我只要维护每个点的子树中与他相连的点的数量即可 用 f[c][u] 表示在 u 的子树中与 u 相连 (假设 u 无色) 且颜色为 c 的点数 查询直接算出与 u 相连的最浅同色节点 a,ans=f[c[u]][a] 考虑修改,我们发现每次 u 被反转,影

bzoj 3566: [SHOI2014]概率充电器 树形DP

首先普及一个概率公式 P(A+B)=P(A)+P(B)-P(AB) 题意:一些充电元件和导线构成一棵树,充电元件是否能充电有2种情况, 1.它自己有qi%的概率充电 2.与它相邻的元件通过导线给它充电(导线有p%的概率导通) 求最终充了电的元件的期望 题解:首先可以将元件能否充电分成3种情况考虑, 1.它自己给自己充好了电 2.它的儿子方向给它传送了电 3.它的父亲方向给它传送了电. 对于1,题目已经给出可以直接赋值, 对于2,可以通过一次树的深度遍历求得.pson[now]=pson[now]

【BZOJ】3566: [SHOI2014]概率充电器

[算法]树型DP+期望DP [题意]一棵树上每个点均有直接充电概率qi%,每条边有导电概率pi%,问期望有多少结点处于充电状态? [题解]引用自:[BZOJ3566][SHOI2014]概率充电器 树形DP 概率DP by 空灰冰魂 最大的难点在于计算每个点充电期望时,两个节点各自的期望都会影响对方的期望. 所以考虑转化对象,改为求每个节点充不上电的期望,充不上电就不用考虑两者的相互影响. fi表示结点i由子结点和自身充不上电的概率 gi表示结点i由父结点充不上电的概率 第一次DFS: hi表示

HDU 6203 ping ping ping [LCA,贪心,DFS序,BIT(树状数组)]

题目链接:[http://acm.hdu.edu.cn/showproblem.php?pid=6203] 题意 :给出一棵树,如果(a,b)路径上有坏点,那么(a,b)之间不联通,给出一些不联通的点对,然后判断最少有多少个坏点. 题解 :求每个点对的LCA,然后根据LCA的深度排序.从LCA最深的点对开始,如果a或者b点已经有点被标记了,那么continue,否者标记(a,b)LCA的子树每个顶点加1. #include<Bits/stdc++.h> using namespace std;

HDU 5542 The Battle of Chibi dp+树状数组

题目:http://acm.hdu.edu.cn/showproblem.php?pid=5542 题意:给你n个数,求其中上升子序列长度为m的个数 可以考虑用dp[i][j]表示以a[i]结尾的长度为j的上升子序列有多少 裸的dp是o(n2m) 所以需要优化 我们可以发现dp的第3维是找比它小的数,那么就可以用树状数组来找 这样就可以降低复杂度 #include<iostream> #include<cstdio> #include<cstring> #include