[ZJOI2008]树的统计Count

来源:互联网 发布:one软件 编辑:程序博客网 时间:2024/05/29 14:06

Description
一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。我们将以下面的形式来要求你对这棵树完成一些操作:
I. CHANGE u t : 把结点u的权值改为t
II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值
III. QSUM u v: 询问从点u到点v的路径上的节点的权值和
注意:从点u到点v的路径上的节点包括uv本身

Input
输入的第一行为一个整数n,表示节点的个数。接下来n1行,每行2个整数ab,表示节点a和节点b之间有一条边相连。接下来n行,每行一个整数,第i行的整数wi表示节点i的权值。接下来1行,为一个整数q,表示操作的总数。接下来q行,每行一个操作,以“CHANGE u t”或者“QMAX u v”或者“QSUM u v”的形式给出。
对于100%的数据,保证1<=n<=300000<=q<=200000;中途操作中保证每个节点的权值w在-30000到30000之间。

Output
对于每个“QMAX”或者“QSUM”的操作,每行输出一个整数表示要求输出的结果。

Sample Input
4
1 2
2 3
4 1
4 2 1 3
12
QMAX 3 4
QMAX 3 3
QMAX 3 2
QMAX 2 3
QSUM 3 4
QSUM 2 1
CHANGE 1 5
QMAX 3 4
CHANGE 3 6
QMAX 3 4
QMAX 2 4
QSUM 3 4

Sample Output
4
1
2
2
10
6
5
6
5
16

HINT

Source

思路
树链剖分模板。
树链剖分不会的请点击这里

代码

#include <cstdio>#include <algorithm>const int maxn=30000;const int inf=1000000000;int n;struct sigment_tree{    private:int maxnum[(maxn<<2)+10],sumnum[(maxn<<2)+10];    private:int updata(int now)    {        maxnum[now]=std::max(maxnum[now<<1],maxnum[now<<1|1]);        sumnum[now]=sumnum[now<<1]+sumnum[now<<1|1];        return 0;    }    public:int build(int now,int left,int right)    {        if(left==right)        {            maxnum[now]=0;            sumnum[now]=0;            return 0;        }        int mid=(left+right)>>1;        build(now<<1,left,mid);        build(now<<1|1,mid+1,right);        updata(now);        return 0;    }    public:int modify(int now,int left,int right,int findnum,int changeval)    {        if((findnum<left)||(findnum>right))        {            return 0;        }        if(left==right)        {            maxnum[now]=changeval;            sumnum[now]=changeval;            return 0;        }        int mid=(left+right)>>1;        modify(now<<1,left,mid,findnum,changeval);        modify(now<<1|1,mid+1,right,findnum,changeval);        updata(now);        return 0;    }    public:int askmax(int now,int left,int right,int askl,int askr)    {        if((askr<left)||(askl>right))        {            return -inf;        }        if((askl<=left)&&(askr>=right))        {            return maxnum[now];        }        int mid=(left+right)>>1;        return std::max(askmax(now<<1,left,mid,askl,askr),askmax(now<<1|1,mid+1,right,askl,askr));    }    public:int asksum(int now,int left,int right,int askl,int askr)    {        if((askr<left)||(askl>right))        {            return 0;        }        if((askl<=left)&&(askr>=right))        {            return sumnum[now];        }        int mid=(left+right)>>1;        return asksum(now<<1,left,mid,askl,askr)+asksum(now<<1|1,mid+1,right,askl,askr);    }};struct tree{    private:int fa[maxn+10],wson[maxn+10],top[maxn+10],dfn[maxn+10],cnt,deep[maxn+10],size[maxn+10];    private:int pre[(maxn<<1)+10],now[maxn+10],son[(maxn<<1)+10],tot;    private:sigment_tree st;    public:int ins(int a,int b)    //将a和b连一条有向边    {        tot++;        pre[tot]=now[a];        now[a]=tot;        son[tot]=b;        return 0;    }    public:int first_dfs(int u,int father)    //首次dfs,将deep、fa、size、wson求出来,为第二次dfs做准备    {        deep[u]=deep[father]+1;        fa[u]=father;        size[u]=1;        wson[u]=0;        int j=now[u];        while(j)        {            int v=son[j];            if(v!=father)            {                first_dfs(v,u);                size[u]+=size[v];                if((!wson[u])||(size[v]>size[wson[u]]))                {                    wson[u]=v;                }            }            j=pre[j];        }        return 0;    }    public:int second_dfs(int u,int father,int topfather)    //第二次dfs,将dfn和top求出来    {        cnt++;        dfn[u]=cnt;        top[u]=topfather;        if(wson[u])        //重儿子的top就是当前节点的top        {            second_dfs(wson[u],u,topfather);        }        int j=now[u];        while(j)        {            int v=son[j];            if((v!=father)&&(v!=wson[u]))            {                second_dfs(v,u,v);            }            j=pre[j];        }        return 0;    }    public:int change(int pos,int val)    //单点修改    {        st.modify(1,1,n,dfn[pos],val);        //直接修改线段树上这个节点就好        return 0;    }    public:int askmax(int x,int y)    //一条路径上求max值    {        int res=-inf;        while(top[x]!=top[y])        {            if(deep[top[x]]<deep[top[y]])            {                std::swap(x,y);            }            res=std::max(res,st.askmax(1,1,n,dfn[top[x]],dfn[x]));            x=fa[top[x]];        }        if(deep[x]>deep[y])        {            std::swap(x,y);        }        //top值相同,那么x和y在同一条重链上        res=std::max(res,st.askmax(1,1,n,dfn[x],dfn[y]));        return res;    }    public:int asksum(int x,int y)    //一条路径上求sum值    {        int res=0;        while(top[x]!=top[y])        {            if(deep[top[x]]<deep[top[y]])            {                std::swap(x,y);            }            res+=st.asksum(1,1,n,dfn[top[x]],dfn[x]);            x=fa[top[x]];        }        if(deep[x]>deep[y])        {            std::swap(x,y);        }        res+=st.asksum(1,1,n,dfn[x],dfn[y]);        return res;    }};tree t;int m;int main(){    scanf("%d",&n);    for(int i=1; i<n; i++)    {        int a,b;        scanf("%d%d",&a,&b);        t.ins(a,b);        t.ins(b,a);    }    t.first_dfs(1,0);    t.second_dfs(1,0,1);    for(int i=1; i<=n; i++)    {        int a;        scanf("%d",&a);        t.change(i,a);    }    scanf("%d",&m);    while(m--)    {        char s[7];        int a,b;        scanf("%s%d%d",s,&a,&b);        if(s[1]=='H')        {            t.change(a,b);        }        if(s[1]=='S')        {            printf("%d\n",t.asksum(a,b));        }        if(s[1]=='M')        {            printf("%d\n",t.askmax(a,b));        }    }    return 0;}