1036: [ZJOI2008]树的统计Count(树链剖分 - 点权最大值&和)

题目传送门:[ZJOI2008]树的统计Count

题目大意:

一棵树上有n个节点,每个节点都有一个权值w。进行以下的一些操作

I. CHANGE u t : 把结点u的权值改为t

II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值

III. QSUM u v: 询问从点u到点v的路径上的节点的权值和

分析:

树链剖分入门题,线段树维护点权最大值mmax和区间和ssum即可

#include
using namespace std;
typedef long long ll;
const int MAX=30005;
#define INF 1<<29
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
int head[MAX],cnt;
int fa[MAX];          //fa[x] x点的父节点 
int deep[MAX];        //deep[x]  记录树中x点的深度 
int size[MAX];        //size[x]  记录x节点的子树节点个数 
int son[MAX];        //son[x]   记录x节点的重儿子 
int top[MAX];        //top[x]   记录x所在重链的顶端节点 
int id[MAX];        //id[x]    记录x节点在线段树中的位置 
int rk[MAX],tot;    //rk[x]       记录在线段树x位置的节点是什么节点,与id[x]相反   
int n,x,y,a[MAX],q,u,v;
char op[10];
struct Tree{
    int mmax,ssum;    //最大值和区间和 
}tree[MAX*4];
struct Edge{
    int next,to;
}edge[MAX*2];
inline void add(int u,int v)
{
    edge[cnt].to=v;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}
inline int max(int a,int b)
{
    return a>b?a:b;
}
void dfs1(int u,int f,int d)    //dfs1求出deep,fa,size,son数组
{
    deep[u]=d;
    fa[u]=f;
    size[u]=1;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v!=fa[u])
        {
            dfs1(v,u,d+1);
            size[u]+=size[v];
            if(son[u]==-1||size[v]>size[son[u]])
                son[u]=v;
        }
    }
}
void dfstop(int u,int t)    //dfstop 划分重链 
{
    top[u]=t;
    id[u]=tot++;
    rk[id[u]]=u;
    if(son[u]==-1)return;
    dfstop(son[u],t);
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v!=son[u]&&v!=fa[u])
            dfstop(v,v);
    }
}
/*线段树部分*/
void PushUp(int rt)
{
    tree[rt].mmax=max(tree[rt<<1].mmax,tree[rt<<1|1].mmax);
    tree[rt].ssum=tree[rt<<1].ssum+tree[rt<<1|1].ssum;
}
void Build(int l,int r,int rt)
{
    if(l==r)
    {
        tree[rt].ssum=a[rk[l]];
        tree[rt].mmax=a[rk[l]];
        return;
    }
    int m=l+r>>1;
    Build(ls),Build(rs);
    PushUp(rt);
}
void Update(int pos,int val,int l,int r,int rt)    //单点更新,将pos位置点值更新为val 
{
    if(l==r)
    {
        tree[rt].mmax=val;
        tree[rt].ssum=val;
        return;
    }
    int m=l+r>>1;
    if(pos<=m)Update(pos,val,ls);
    else Update(pos,val,rs);
    PushUp(rt);
}
int QMAX(int L,int R,int l,int r,int rt)        //查询[l,r]区间最大值 
{
    if(L<=l&&r<=R)
        return tree[rt].mmax;
    int m=l+r>>1;
    int ans=-INF;
    if(L<=m)ans=max(ans,QMAX(L,R,ls));
    if(R>m)ans=max(ans,QMAX(L,R,rs));
    return ans;
}
int QSUM(int L,int R,int l,int r,int rt)        //查询[l,r]区间和 
{
    if(L<=l&&r<=R)
        return tree[rt].ssum;
    int m=l+r>>1;
    int ans=0;
    if(L<=m)ans+=QSUM(L,R,ls);
    if(R>m)ans+=QSUM(L,R,rs);
    return ans;
}
int solveMax(int x,int y)            //查询x->y路径节点最大值 
{
    int res=-INF;
    while(top[x]!=top[y])
    {
        if(deep[top[x]]<deep[top[y]])
            swap(x,y);
        res=max(res,QMAX(id[top[x]],id[x],1,tot,1));
        x=fa[top[x]];
    }
    if(id[x]>id[y])swap(x,y);
    res=max(res,QMAX(id[x],id[y],1,tot,1));
    return res;
}
int solveSum(int x,int y)        //查询x->y路径节点的权值和 
{
    int res=0;
    while(top[x]!=top[y])
    {
        if(deep[top[x]]<deep[top[y]])
            swap(x,y);
        res+=QSUM(id[top[x]],id[x],1,tot,1);
        x=fa[top[x]];
    }
    if(id[x]>id[y])swap(x,y);
    res+=QSUM(id[x],id[y],1,tot,1);
    return res;
}
void init()
{
    memset(head,-1,sizeof(head));cnt=0;
    memset(son,-1,sizeof(son));tot=1;
    memset(a,0,sizeof(a));
    memset(tree,0,sizeof(tree));
}
int main()
{
    init();
    scanf("%d",&n);
    for(int i=1;i)
    {
        scanf("%d%d",&u,&v);
        add(u,v);add(v,u);
    }
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
        
    dfs1(1,-1,0);dfstop(1,1);    
    Build(1,tot,1);
    scanf("%d",&q);
    while(q--)
    {
        scanf("%s",op);
        scanf("%d%d",&x,&y);
        if(op[1]=='M')printf("%d\n",solveMax(x,y));
        if(op[1]=='S')printf("%d\n",solveSum(x,y));
        if(op[1]=='H')Update(id[x],y,1,tot,1);
    }
    return 0;
}

总结:很普通的树链剖分裸题,但是写的时候莫名其妙挂掉..一直无法通过,最后莫名其妙又过了,很奇怪QAQ

你可能感兴趣的:(1036: [ZJOI2008]树的统计Count(树链剖分 - 点权最大值&和))