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

3553: [Shoi2014]三叉神经树

Time Limit: 160 Sec  Memory Limit: 256 MB
Submit: 347  Solved: 112
[Submit][Status][Discuss]

Description

计算神经学作为新兴的交叉学科近些年来一直是学术界的热点。一种叫做SHOI 的神经组织因为其和近日发现的化合物 SHTSC 的密切联系引起了人们的极大关注。
SHOI 组织由若干个 SHOI 细胞构成,SHOI 细胞之间形成严密的树形结构。
每个 SHOI 细胞都有且只有一个输出端,被称为轴突,除了一个特殊的、被称为根细胞的 SHOI 细胞的输出作为整个组织的输出以外,其余细胞的轴突均连向其上级 SHOI 细胞;并且有且只有三个接收端,被称为树突,从其下级细胞或者其它神经组织那里接收信息。SHOI 细胞的信号机制较为简单,仅有 0 和 1 两种。每个 SHOI 细胞根据三个输入端中 0 和 1 信号的多寡输出较多的那一种。
现在给出了一段 SHOI 组织的信息,以及外部神经组织的输入变化情况。请你模拟 SHOI 组织的输出结果。

Input

第一行一个整数:n。表示 SHOI 组织的总细胞个数。SHOI 细胞由 1~n 编号,编号为 1 的是根细胞。
从第二行开始的 n 行,每行三个整数 x1, x2, x3,分别表示编号为 1~n 的 SHOI 细胞的树突连接。1<xi≤n 表示连向编号为 xi 的细胞的轴突, n<xi≤3n+1 表示连向编号为 xi 的外界输入。输入数据保证给出的 SHOI 组织是合法的且所有的 xi 两两不同。
接下来一行 2n+1 个 0/1 的整数,表示初始时的外界输入。
第 n+3 行有一个整数:q,表示总操作数。
之后 q 行每行一个整数 x,表示编号为 x 的外界输入发生了变化。

Output

输出 q 行每行一个整数,对应第 i 次外界输入变化后的根细胞的输出。

Sample Input

3
2 3 4
5 6 7
8 9 10
0 0 0 0 1 1 1
5
4
4
5
6
8

Sample Output

1
0
0
1
1

HINT

对于 100%的数据,n≤500000,q≤500000。

Source

By 佚名提供

Solution

这题还真是不错。

首先题意就是说,一共有N个节点,然后有其余2*N+1个节点,这些节点成三叉树,然后2*N+1个点有初始0/1值,定义一个非叶子节点的值为它三个孩子中0/1较多的。每次改变2*N+1个节点中的一个0/1值,并输出根节点的值是多少。

这么一看我们发现,对于一个非叶子节点,它的权值状态是可数的000,100,010,001,110,101,011,111,我们不妨设状态为val,那么val=2/3时值为1,val=0/1时值为0

对一个叶子节点权值取反,则它可能直接影响到它的fa,它的fa又可能会影响到fa到root这个路径上的一段节点。

实际上,只有在它的fa的val从2->1或者1->2的时候会对它到root的路径上的点可能产生影响。

那么我们用树链剖分去维护这N个非叶子节点。对于每个区间,我们维护最长的靠右的连续val=1/2的长度

树链在线段树上是从上到下按从左到右存的,而我们每次会影响到的,就是fa->root中连续的与fa直接相连的那段1/2;因为显然fa的改变这会引起他们的改变。,所以这里是靠右的连续的长度

就是说当fa.val=1->2它到root这条路径上,与它直接相连的1都会更改成2;当val=2->1时同理。

我们在覆盖完这连续的区间后,对于区间上面那个点也进行单点修改,其效果大致类似于修改。

同样是可以维护连续靠右的val=1/2的左端,但是树链剖分中链在线段树中是连续的,所以其实只记录长度即可。

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<‘0‘ || ch>‘9‘) {if (ch==‘-‘) f=-1; ch=getchar();}
    while (ch>=‘0‘ && ch<=‘9‘) {x=x*10+ch-‘0‘; ch=getchar();}
    return x*f;
}
#define MAXN 500010
int N,Q,val[MAXN*3],fa[MAXN*3],V[MAXN];
struct EdgeNode{int next,to;}edge[MAXN<<1];
int head[MAXN],cnt=1;
void AddEdge(int u,int v) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v;}
void InsertEdge(int u,int v) {fa[v]=u; if (v>N) return; AddEdge(u,v); AddEdge(v,u);}
int size[MAXN],deep[MAXN],pl[MAXN],dfn,pr[MAXN],pre[MAXN],top[MAXN],son[MAXN];
namespace SegmentTree
{
    //len[1]left[1]表示连续val=1,len[2]left[2]表示连续val=2
    struct SegmentTreeNode{int l,r,size,val,tag,len[3];}tree[MAXN<<2];
    #define ls now<<1
    #define rs now<<1|1
    inline void Merge(SegmentTreeNode &rt,SegmentTreeNode lson,SegmentTreeNode rson)
    {
        for (int i=1; i<=2; i++)
            if (rson.len[i]==rson.size) rt.len[i]=lson.len[i]+rson.len[i];
                else rt.len[i]=rson.len[i];
    }
    inline void Update(int now) {Merge(tree[now],tree[ls],tree[rs]);}
    inline void cover(int now,int D)
    {
        tree[now].val=D; tree[now].tag=D; tree[now].len[1]=tree[now].len[2]=0;
        if (D==1) tree[now].len[1]=tree[now].size;
        if (D==2) tree[now].len[2]=tree[now].size;
    }
    inline void PushDown(int now)
    {
        if (tree[now].l==tree[now].r || tree[now].tag==-1) return;
        int delta=tree[now].tag;
        cover(ls,delta); cover(rs,delta); tree[now].tag=-1;
    }
    inline void BuildTree(int now,int l,int r)
    {
        tree[now].l=l; tree[now].r=r; tree[now].size=r-l+1; tree[now].tag=-1;
        tree[now].len[1]=tree[now].len[2]=0;
        if (l==r)
            {
                tree[now].val=V[l];
                if (tree[now].val==1) tree[now].len[1]=1;
                if (tree[now].val==2) tree[now].len[2]=1;
                return;
            }
        int mid=(l+r)>>1;
        BuildTree(ls,l,mid); BuildTree(rs,mid+1,r);
        Update(now);
    }
    inline void Cover(int now,int L,int R,int D)
    {
        int l=tree[now].l,r=tree[now].r;
        PushDown(now);
        if (L<=l && R>=r) {cover(now,D); return;}
        int mid=(l+r)>>1;
        if (L<=mid) Cover(ls,L,R,D);
        if (R>mid) Cover(rs,L,R,D);
        Update(now);
    }
    inline SegmentTreeNode Query(int now,int L,int R)
    {
        SegmentTreeNode rl,rr,re; bool fl=0,fr=0;
        int l=tree[now].l,r=tree[now].r;
        PushDown(now);
        if (L<=l && R>=r) return tree[now];
        int mid=(l+r)>>1;
        if (L<=mid) fl=1,rl=Query(ls,L,R);
        if (R>mid) fr=1,rr=Query(rs,L,R);
        if (fl && !fr) return rl;
        if (!fl && fr) return rr;
        if (fl && fr) return Merge(re,rl,rr),re;
    }
}
using namespace SegmentTree;
inline void DFS_1(int now)
{
    size[now]=1;
    for (int i=head[now]; i; i=edge[i].next)
        if (edge[i].to!=fa[now])
            {
                deep[edge[i].to]=deep[now]+1;
                DFS_1(edge[i].to);
                size[now]+=size[edge[i].to];
                if (size[son[now]]<size[edge[i].to]) son[now]=edge[i].to;
            }
}
inline void DFS_2(int now,int chain)
{
    pl[now]=++dfn; pre[dfn]=now; top[now]=chain;
    if (son[now]) DFS_2(son[now],chain);
    for (int i=head[now]; i; i=edge[i].next)
        if (edge[i].to!=fa[now] && edge[i].to!=son[now])
            DFS_2(edge[i].to,edge[i].to);
    pr[now]=dfn;
}
void DFS(int now)
{
    for (int i=head[now]; i; i=edge[i].next)
        if (edge[i].to!=fa[now])
            DFS(edge[i].to),val[now]+=val[edge[i].to]>=2? 1:0;
}
void Solve(int x,int y)
{
    int L,R,len,t,last,now; bool f,flag=0;
    if (val[x]==1) f=1; else f=0; val[x]^=1; last=val[fa[x]]; val[fa[x]]+=f? -1:1; now=val[fa[x]];
    t=x=fa[x];
    if (now==0 || now==3) {SegmentTree::Cover(1,t,t,now); printf("%d\n",SegmentTree::Query(1,1,1).val>=2? 1:0); return;}
    if (now==1 && last==0 || now==2 && last==3) {SegmentTree::Cover(1,t,t,now); printf("%d\n",SegmentTree::Query(1,1,1).val>=2? 1:0); return;}
    if (now==1 && last==2) f=1; else f=0;
    while (top[x]!=top[y])
        {
//            printf("Now(%d  ,  %d)\n",x,y);
            if (deep[top[x]]<deep[top[y]]) swap(x,y);
            L=top[x],R=x; SegmentTreeNode t=Query(1,L,R);
            if (f) len=t.len[2]; else len=t.len[1];
            if (len==R-L+1) SegmentTree::Cover(1,L,R,f? 1:2);
                else {L=L-len+1,SegmentTree::Cover(1,L,R,f? 1:2); flag=1; break;}
            x=fa[top[x]];
        }
    if (!flag)
        {
            if (deep[x]>deep[y]) swap(x,y);
            L=x,R=y; SegmentTreeNode t=Query(1,L,R);
            if (f) len=t.len[2]; else len=t.len[1];
            if (len==R-L+1) SegmentTree::Cover(1,L,R,f? 1:2); else L=L-len+1,SegmentTree::Cover(1,L,R,f? 1:2);
        }
//    puts("===========================================");
//    for (int i=1; i<=5; i++) printf("%d %d %d %d %d %d %d\n",i,tree[i].l,tree[i].r,tree[i].size,tree[i].val,tree[i].len[1],tree[i].len[2]);
    t=fa[pre[L]]; if (t) SegmentTree::Cover(1,t,t,f? val[t]-1:val[t]+1); val[t]+=f? -1:1;
    printf("%d\n",SegmentTree::Query(1,1,1).val>=2? 1:0);
}
int main()
{
    N=read();
    for (int x,y,z,i=1; i<=N; i++)
        x=read(),y=read(),z=read(),InsertEdge(i,x),InsertEdge(i,y),InsertEdge(i,z);
    for (int i=1; i<=2*N+1; i++) val[N+i]=read(),val[fa[N+i]]+=val[N+i];
    Q=read();
    DFS(1);
    DFS_1(1); DFS_2(1,1);
    for (int i=1; i<=N; i++) V[pl[i]]=val[i];
    SegmentTree::BuildTree(1,1,N);
    while (Q--) {int pos=read(); Solve(pos,1);}
    return 0;
}

没调完

时间: 2024-10-13 08:55:39

【BZOJ-3553】三叉神经树 树链剖分的相关文章

CF487E Tourists 圆方树、树链剖分

传送门 注意到我们需要求的是两点之间所有简单路径中最小值的最小值,那么对于一个点双联通分量来说,如果要经过它,则一定会经过这个点双联通分量里权值最小的点 注意:这里不能缩边双联通分量,样例\(2\)就是一个反例 上面这个图如果缩点双会缩成\(3\)个,但是缩边双会将整个图缩成\(1\)个点. 假如我们询问的是\((1,4)\)之间的简单路径,而图中权值最小的点为\(7\)号点,那么如果缩成了边双联通分量,你的答案会是\(7\)号点的权值,意即认为可以走到\(7\)号点,但实际上如果到\(7\)号

【GDOI2020模拟01.16】树上的鼠 (博弈+长链剖分优化dp)

https://gmoj.net/senior/#contest/show/2989/2 思考什么时候先手会赢. 一开始双方都不会希望走到直径的端点上,因为那样对方就可以走直径而使自己输掉. 删掉直径的端点,考虑剩下的树的子问题. 如果又走到端点去了,对面就走到另外一个端点,那我就走到下一层的直径端点去了. 所以大家一直都不想走到直径端点. 一直删,如果最后只剩1一个点,说明先手必败,否则先手必胜. 如果是一条链,就是链的两边的长度不等先手就必胜. 如果是一棵树,考虑随便找一条直径,每次删去它的

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

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

BZOJ 2243: [SDOI2011]染色 树链剖分

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1886  Solved: 752[Submit][Status] Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写一个程序依次完成这m个操作. In

BZOJ 2243 染色(树链剖分好题)

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MB Submit: 7971  Solved: 2990 [Submit][Status][Discuss] Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写一个程序依

【BZOJ】1146: [CTSC2008]网络管理Network(树链剖分+线段树套平衡树+二分 / dfs序+树状数组+主席树)

第一种做法(时间太感人): 这题我真的逗了,调了一下午,疯狂造数据,始终找不到错. 后来发现自己sb了,更新那里没有打id,直接套上u了.我.... 调了一下午啊!一下午的时光啊!本来说好中午A掉去学习第二种做法,噗 好吧,现在第一种做法是hld+seg+bst+二分,常数巨大,log^4级别,目前只会这种. 树剖后仍然用线段树维护dfs序区间,然后在每个区间建一颗平衡树,我用treap,(这题找最大啊,,,囧,并且要注意,这里的rank是比他大的数量,so,我们在二分时判断要判断一个范围,即要

BZOJ 3083 遥远的国度 树链剖分

3083: 遥远的国度 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 797  Solved: 181[Submit][Status] Description 描述 zcwwzdjn在追杀十分sb的zhx,而zhx逃入了一个遥远的国度.当zcwwzdjn准备进入遥远的国度继续追杀时,守护神RapiD阻拦了zcwwzdjn的去路,他需要zcwwzdjn完成任务后才能进入遥远的国度继续追杀. 问题是这样的:遥远的国度有n个城市,这些城市之间由一些路连

bzoj 3083 树链剖分

首先我们先将树提出一个根变成有根树,那么我们可以通过树链剖分来实现对于子树的最小值求解,那么按照当前的根和询问的点的相对位置关系我们可以将询问变成某个子树和或者除去某颗子树之后其余的和,前者直接询问区间,后者询问区间的补集. /************************************************************** Problem: 3083 User: BLADEVIL Language: C++ Result: Accepted Time:6412 ms

【BZOJ 1036】树的统计Count(树链剖分)

[BZOJ 1036]树的统计Count(树链剖分) 1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 12991  Solved: 5233 Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成 一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权