BZOJ 1036: [ZJOI2008]树的统计Count(树链剖分(点权))

1036: [ZJOI2008]树的统计Count

Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 16631 Solved: 6756
[Submit][Status][Discuss]
Description

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

Input

  输入的第一行为一个整数n,表示节点的个数。接下来n – 1行,每行2个整数a和b,表示节点a和节点b之间有
一条边相连。接下来n行,每行一个整数,第i行的整数wi表示节点i的权值。接下来1行,为一个整数q,表示操作
的总数。接下来q行,每行一个操作,以“CHANGE u t”或者“QMAX u v”或者“QSUM u v”的形式给出。
对于100%的数据,保证1<=n<=30000,0<=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
题解:树链剖分后,用一棵线段树维护最大值和和值。
代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int cnt,u,v,k,tot,n,q;
int dep[N],fa[N],siz[N],son[N];
int sum[N<<2],mx[N<<2];
int a[N];
struct node{int to,next;}edge[N<<2];
int head[N<<2];
void add(int u,int v)
{
    edge[cnt].to=v,edge[cnt].next=head[u],head[u]=cnt++;
    edge[cnt].to=u,edge[cnt].next=head[v],head[v]=cnt++;
}
void dfs1(int u,int f,int d)
{
    dep[u]=d,fa[u]=f,siz[u]=1,son[u]=0;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v==f)continue;
        dfs1(v,u,d+1);
        siz[u]+=siz[v];
        if(siz[son[u]]<siz[v]) son[u]=v;
    }
}
int top[N],pre[N],tree[N];
void dfs2(int u,int tp)
{
    top[u]=tp,tree[u]=++tot,pre[tree[u]]=u;
    if(!son[u]) return ;
    dfs2(son[u],tp);
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v==fa[u]||v==son[u]) continue;
        dfs2(v,v);
    }

}
void init()
{
    tot=0;
    cnt=0;
    memset(head,-1,sizeof(head));
}
void pushup(int rt)
{
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
    mx[rt]=max(mx[rt<<1],mx[rt<<1|1]);
}
void build(int l,int r,int rt)
{
    if(l==r)
    {
        sum[rt]=mx[rt]=a[pre[l]];
        return ;
    }
    int mid=(r+l)>>1;
    build(l,mid,rt<<1);
    build(mid+1,r,rt<<1|1);
    pushup(rt);
}
void update(int l,int r,int pos,int val,int rt)
{
    if(l==r)
    {
        sum[rt]=mx[rt]=val;
        return ;
    }
    int mid=(r+l)>>1;
    if(pos<=mid) update(l,mid,pos,val,rt<<1);
    else update(mid+1,r,pos,val,rt<<1|1);
    pushup(rt);
}
int query_max(int l,int r,int ll,int rr,int rt)
{
    if(ll<=l&&rr>=r)
    {
        return mx[rt];
    }
    int ans=-1e8;
    int mid=(r+l)>>1;
    if(ll<=mid)
        ans=max(ans,query_max(l,mid,ll,rr,rt<<1));
    if(rr>mid) ans=max(ans,query_max(mid+1,r,ll,rr,rt<<1|1));
    return ans;
}
int query_sum(int l,int r,int ll,int rr,int rt)
{
    if(ll<=l&&rr>=r)
    {
        return sum[rt];
    }
    int ans=0;
    int mid=(r+l)>>1;
    if(ll<=mid)
        ans+=query_sum(l,mid,ll,rr,rt<<1);
    if(rr>mid) ans+=query_sum(mid+1,r,ll,rr,rt<<1|1);
    return ans;
}
int find_sum(int x,int y)
{
    int ans=0;
    int fx=top[x],fy=top[y];
    while(fx!=fy)
    {
        if(dep[fx]<dep[fy]) swap(x,y),swap(fx,fy);
        ans+=query_sum(1,n,tree[fx],tree[x],1);
        x=fa[fx],fx=top[x];
    }
    if(dep[x]>dep[y]) swap(x,y);
    ans+=query_sum(1,n,tree[x],tree[y],1);
    return ans;
}
int find_max(int x,int y)
{
    int ans=-1e6;
    int fx=top[x],fy=top[y];
    while(fx!=fy)
    {
        if(dep[fx]<dep[fy]) swap(x,y),swap(fx,fy);
        ans=max(ans,query_max(1,n,tree[fx],tree[x],1));
        x=fa[fx],fx=top[x];
    }
    if(dep[x]>dep[y]) swap(x,y);
    ans=max(ans,query_max(1,n,tree[x],tree[y],1));
    return ans;
}
char str[10];
int main()
{
    init();
    scanf("%d",&n);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&u,&v);
        add(u,v);
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    dfs1(1,0,1),dfs2(1,1);
    build(1,n,1);
    scanf("%d",&q);
    while(q--)
    {
        scanf("%s%d%d",str,&u,&v);
        if(str[0]=='C')
        {
            update(1,n,tree[u],v,1);
            a[u]=v;

        }
        else if(str[1]=='M')
        {
            printf("%d\n",find_max(u,v));

        }
        else
        {
            printf("%d\n",find_sum(u,v));

        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值