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

Posted DaD3zZ

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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;
}
没调完

以上是关于BZOJ-3553三叉神经树 树链剖分的主要内容,如果未能解决你的问题,请参考以下文章

[bzoj3553] [Shoi2014]三叉神经树

树链剖分详解

树链剖分

LibreOJ #139 树链剖分 [树链剖分,线段树]

树链剖分总结线段树维护

树链剖分详解