BZOJ 1036 [ZJOI2008]树的统计Count(动态树)

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1036

题意:一棵树,每个节点有一个权值。三种操作:(1)修改某个节点的权值;(2)输出某两个节点之间的权值之和;(3)输出某两个节点之间权值的最大值。

思路:(1)首先说明,在splay中记录一个father,表示当前节点的父节点。但是在这里,在一个树链中,father与在splay中的father的意义是一样的,也就是设v的father是u,那么u的左孩子或者右孩子必然有一个是v。但是,若v和u不在一个树链中,那么u表示v所在树链的最上面的顶点的父节点。也就是此时u的左孩子和右孩子都不是v,u和v属于两个树链;

(2)在一个树链中v的左孩子是在v上面的顶点,也就是在原树中这些点都是v的父节点以及祖宗节点;右孩子是v下面的顶点,也就是原树中v的孩子以及子孙节点。当然不管右孩子还是左孩子都是当前与v在一个树链中的;

(3)splay(x)将x旋转到x所在的树链的根,access(x)将x和root(这个root才是真正的树根)的边变为实边。在access(x)时,首先要断开x与其右孩子Xr的关联(在(4)中我们解释为什么要断开),并将Xr的father设为x,那么此时,Xr将成为一个树链的根;接着对于v的父节点u,因为要将u的右孩子变为v,所以之前u的右孩子(若有)Ur要与其断开并将Ur的father设为u,此时Ur将成为其所在树链的根;接着将u的右孩子设为v。一直向上直到root;

(4)每次计算(x,y)的最大或者和时,首先access(x)将x和root之间的边变为实边,然后access(y)此时返回值就是p=Lca(x,y)。想想为什么是这样?因为Lca(x,y)已经跟x在一个树链上了,因为我们已经access(x)了。现在从y开始向上找时,对于其father节点z,首先会splay(z)将z转到其所在树链的根节点,那么若z是Lca(x,y),那么z必然将成为树根,也就是root,那么其father节点为null。现在我们说明白了返回值为什么是 Lca(x,y)。接着p会与x断开,因为x在p的右孩子或者右孩子以下,p的右孩子将变成y所以断开了。此时若将splay(x),那么x就是p到x路径(不包含p)组成的树链的根,并且不会包含x以下的部分,在(3)中我们知道,x向上access时x与其右孩子已经断开。那么用x的sum以及p的val以及p的右孩子也就是y的sum就能计算出x到y的和。

 
   






struct node

{

    int Max,sum,son[2],val,father;

};



const int N=500005;

vector<int> g[N];

int n,m;

node a[N];



void up(int x)

{

    if(!x) return;

    int L=a[x].son[0];

    int R=a[x].son[1];

    a[x].sum=a[x].val+a[L].sum+a[R].sum;

    a[x].Max=max(a[x].val,max(a[L].Max,a[R].Max));

}



int isRoot(int x)

{

    int p=a[x].father;

    return a[p].son[0]!=x&&a[p].son[1]!=x;

}





void rotate(int x,int k)

{

    int y=a[x].father,temp;

    temp=a[y].son[k]=a[x].son[!k];

    a[temp].father=y;

    a[x].father=a[y].father;

    temp=a[y].father;

    if(a[temp].son[0]==y) a[temp].son[0]=x;

    else if(a[temp].son[1]==y) a[temp].son[1]=x;

    a[y].father=x;

    a[x].son[!k]=y;

    up(y);

    up(x);

}



void splay(int x)

{

    int y,z;

    while(!isRoot(x))

    {

        y=a[x].father;

        z=a[y].father;

        if(isRoot(y))

        {

            if(a[y].son[0]==x) rotate(x,0);

            else rotate(x,1);

        }

        else if(a[z].son[0]==y)

        {

            if(a[y].son[0]==x) rotate(y,0);

            else rotate(x,1);

            rotate(x,0);

        }

        else

        {

            if(a[y].son[1]==x) rotate(y,1);

            else rotate(x,0);

            rotate(x,1);

        }

    }

}





int access(int x)

{

    int y=0;

    while(x)

    {

        splay(x);

        a[x].son[1]=y;

        up(x);



        y=x;

        x=a[x].father;

    }

    return y;

}



int getSum(int x,int y)

{

    access(x);

    int lca=access(y);

    splay(x);

    int ans=a[lca].val+a[a[lca].son[1]].sum;

    if(x!=lca) ans+=a[x].sum;

    return ans;

}



int getMax(int x,int y)

{

    access(x);

    int lca=access(y);

    splay(x);

    int ans=max(a[lca].val,a[a[lca].son[1]].Max);

    if(x!=lca) ans=max(ans,a[x].Max);

    return ans;

}



void change(int x,int val)

{

    access(x);

    a[x].val=val;

    up(x);

}



void BFS()

{

    queue<int> Q;

    int visit[N]={0},i,u,v;

    Q.push(1);

    visit[1]=1;

    while(!Q.empty())

    {

        u=Q.front();

        Q.pop();



        FOR0(i,SZ(g[u]))

        {

            v=g[u][i];

            if(visit[v]) continue;

            a[v].father=u;

            visit[v]=1;

            Q.push(v);

        }

    }

}





int main()

{

    RD(n);

    int i,u,v;

    clr(a,0);

    a[0].Max=-1e9;

    FOR1(i,n-1)

    {

        RD(u,v);

        g[u].pb(v),g[v].pb(u);

    }

    FOR1(i,n) RD(a[i].val);

    BFS();

    RD(m);

    char op[10];

    while(m--)

    {

        RD(op);

        RD(u,v);

        if(op[1]=='H') change(u,v);

        else if(op[1]=='S') PR(getSum(u,v));

        else PR(getMax(u,v));

    }

    return 0;

}

 
   

  

 
  

你可能感兴趣的:(count)