BZOJ 1036 [ZJOI2008]树的统计 Count 题解&代码

题意:一棵树上有n个节点,编号1到n,每个节点i有权值w[i]。
有三种操作:
I. CHANGE u t : 把结点u的权值改为t
II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值
III. QSUM u v: 询问从点u到点v的路径上的节点的权值和
注意:从点u到点v的路径上的节点包括u和v本身

题目非常浅显易懂= =线段树可以用maxv[]和sum[]保存状态。
change操作就是线段树的单点修改维护
qmax和qsum操作是树链剖分的基本操作了…总之在树上的某一条连续的链上乱搞的八层是树链剖分

关于树链剖分,今天懒得总结= =反正也算是过了两道题了?大致的写法也基本形成了…
嗯,下一道树链剖分一定写总结= =【这是一个机智的flag
**经常把in[x]和x代表的含义搞错= =于是WA了三次,从昨天做到今天

#include<iostream>
#include<vector>
#include<stdio.h>
#define lson (o<<1)
#define rson ((o<<1)|1)
using namespace std;
const int maxn=30005;
int n,q,fa[maxn],a,b,x,y,tot=0;
int maxv[maxn*4],son[maxn],rt[maxn],size[maxn];
int in[maxn],fin[maxn],out[maxn],deep[maxn],v[maxn];
long long ans,sum[maxn*4];
vector <int> edge[maxn];
char s[10];
void dfs(int r,int pre)
{
    fa[r]=pre;
    size[r]=1;
    son[r]=-1;
    deep[r]=deep[pre]+1;
    for(int i=0;i<edge[r].size();i++)
    if(edge[r][i]!=pre)
    {
        dfs(edge[r][i],r);
        size[r]+=size[edge[r][i]];
        if(son[r]==-1 || size[edge[r][i]]>size[son[r]])
            son[r]=edge[r][i];
    }
}
void init(int c,int root)
{
    in[c]=++tot;
    fin[in[c]]=c;
    rt[c]=root;
    if(son[c]==-1)return;
    init(son[c],root);
    for(int i=0;i<edge[c].size();i++)
        if(edge[c][i]!=son[c] && edge[c][i]!=fa[c])
            init(edge[c][i],edge[c][i]);
    out[c]=tot;
}
void maintain(int o,int l,int r)
{
    if(l!=r)
    {
        sum[o]=sum[lson]+sum[rson];
        maxv[o]=max(maxv[lson],maxv[rson]);
    }
}
void buildtree(int o,int l,int r)
{
    if(l==r)
    {
        sum[o]=v[fin[l]];
        maxv[o]=v[fin[l]];
        return;
    }
    int mid=(l+r)/2;
    buildtree(lson,l,mid);
    buildtree(rson,mid+1,r);
    maintain(o,l,r);
}
void addtree(int o,int l,int r,int L,int R,int c)
{
    if(l>R || r<L)return;
    if(l==r)
    {
        sum[o]=c;
        maxv[o]=c;
        return;
    }
    int mid=(l+r)/2;
    addtree(lson,l,mid,L,R,c);
    addtree(rson,mid+1,r,L,R,c);
    maintain(o,l,r);
}
long long getmax(int o,int l,int r,int L,int R)
{
    if(l>R || r<L)return -30005;
    if(l>=L && r<=R) return maxv[o];
    int mid=(l+r)/2;
    return max(getmax(lson,l,mid,L,R),getmax(rson,mid+1,r,L,R));
}
void getsum(int o,int l,int r,int L,int R)
{
    if(l>R || r<L)return;
    if(l>=L && r<=R)
    {
        ans+=sum[o];
        return;
    }
    int mid=(l+r)/2;
    getsum(lson,l,mid,L,R);
    getsum(rson,mid+1,r,L,R);
}
void findmax(int x,int y)
{
    while(rt[x]!=rt[y])
    {
        if(deep[rt[x]]>deep[rt[y]])
            ans=max(ans,getmax(1,1,n,in[rt[x]],in[x])),x=fa[rt[x]];
        else ans=max(ans,getmax(1,1,n,in[rt[y]],in[y])),y=fa[rt[y]];
    }
    if(deep[x]>deep[y])ans=max(ans,getmax(1,1,n,in[y],in[x]));
    else ans=max(ans,getmax(1,1,n,in[x],in[y]));
}
void findsum(int x,int y)
{
    while(rt[x]!=rt[y])
    {
        if(deep[rt[x]]>deep[rt[y]])
            getsum(1,1,n,in[rt[x]],in[x]),x=fa[rt[x]];
        else getsum(1,1,n,in[rt[y]],in[y]),y=fa[rt[y]];
    }
    if(deep[x]>deep[y])getsum(1,1,n,in[y],in[x]);
    else getsum(1,1,n,in[x],in[y]);
}
int main(void)
{
    scanf("%d",&n);
    for(int i=2;i<=n;i++)
    {
        scanf("%d%d",&a,&b);
        edge[a].push_back(b);
        edge[b].push_back(a);
    }
    deep[1]=1;
    dfs(1,0);
    for(int i=1;i<=n;i++)
        scanf("%d",&v[i]);
    init(1,1);
    buildtree(1,1,n);
    scanf("%d",&q);
    for(int i=1;i<=q;i++)
    {
        scanf("%s",s);
        scanf("%d%d",&x,&y);
        if(s[0]=='C')
            addtree(1,1,n,in[x],in[x],y);
        else
        {
            if(s[1]=='M')ans=-10000005,findmax(x,y);
            else ans=0,findsum(x,y);
            printf("%d\n",ans);
        }
    }
    return 0;
}

你可能感兴趣的:(题解,C++,树链剖分,bzoj)