BZOJ1036: [ZJOI2008]树的统计Count

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的路径上的节点包括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
Sample Output
4
1
2
2
10
6
5
6
5
16
HINT
Source
树的分治

遥想当年我刚知道BZOJ的时候,迫切想做几道题一展身手,于是就专挑看得懂题面的题来做(无语了),于是。。。那时的我对于这道题交了一个O(n)的纯暴力(当时连时间复杂度都懒得算),而且好像还非常激动得写了个Tarjan求公共祖先,T掉之后愣了好久。。。
言归正传,此题做法:树的分治、树链剖分或动态树,好像还有分块的做法(ORZ)。我看着提示里面写着树的分治然后写了个树链剖分加线段树的做法,然后基本上就是模板了。
一定要注意询问同一个节点的情况(QMAX x x,QSUM x x),可以直接特判来解决。

#include<cstdio>
#include<algorithm>
#include<cstring>
#define INF 2147483647
using namespace std;
const int N=30100;
const int T=5000010;
int n,m,q,w[N],point[N],next[2*N],to[2*N];
int treenum,nodenum,deep[N],fa[N],son[N],size[N];
int rank[N],top[N],belong[N];char opt[20];
int root[N],lch[T],rch[T],treemax[T],treesum[T];
void add(int u,int v){++m;next[m]=point[u];point[u]=m;to[m]=v;}
void dfs(int u)
{
    deep[u]=deep[fa[u]]+1;
    int tmp=0;size[u]=1;
    for (int i=point[u];i;i=next[i])
    {
        int v=to[i];
        if (v!=fa[u])
        {
            fa[v]=u;dfs(v);size[u]+=size[v];
            if (size[v]>tmp){tmp=size[v];son[u]=v;}
        }
    }
    for (int i=point[u];i;i=next[i])
    {
        int v=to[i];
        if (v==son[u])
        {
            belong[u]=belong[v];
            rank[u]=rank[v]+1;
        }
        else top[belong[v]]=v;
    }
    if (son[u]==0)
    {
        belong[u]=++treenum;
        rank[u]=1;
    }
}
void updata(int now)
{
    treemax[now]=max(treemax[lch[now]],treemax[rch[now]]);
    treesum[now]=treesum[lch[now]]+treesum[rch[now]];
}
void build(int now,int l,int r)
{
    treemax[now]=-INF;
    if (l==r) return;
    int mid=(l+r)>>1;
    build(lch[now]=++nodenum,l,mid);
    build(rch[now]=++nodenum,mid+1,r);
}
void change(int now,int l,int r,int loc,int v)
{
    if (l==r)
    {
        treemax[now]=treesum[now]=v;
        return;
    }
    int mid=(l+r)>>1;
    if (loc<=mid) change(lch[now],l,mid,loc,v);
    else change(rch[now],mid+1,r,loc,v);
    updata(now);
}
int maxquery(int now,int l,int r,int lrange,int rrange)
{
    if (lrange<=l&&r<=rrange) return treemax[now];
    int mid=(l+r)>>1,ans=-INF;
    if (lrange<=mid) ans=max(ans,maxquery(lch[now],l,mid,lrange,rrange));
    if (rrange>mid) ans=max(ans,maxquery(rch[now],mid+1,r,lrange,rrange));
    return ans;
}
int sumquery(int now,int l,int r,int lrange,int rrange)
{
    if (lrange<=l&&r<=rrange) return treesum[now];
    int mid=(l+r)>>1,ans=0;
    if (lrange<=mid) ans+=sumquery(lch[now],l,mid,lrange,rrange);
    if (rrange>mid) ans+=sumquery(rch[now],mid+1,r,lrange,rrange);
    return ans;
}
int maxquery1(int u,int v)
{
    int ans=-INF;
    while(belong[u]!=belong[v])
    {
        if (deep[top[belong[u]]]>deep[top[belong[v]]])
        {
            ans=max(ans,maxquery(root[belong[u]],1,rank[top[belong[u]]],rank[u],rank[top[belong[u]]]));
            u=fa[top[belong[u]]];
        }
        else
        {
            ans=max(ans,maxquery(root[belong[v]],1,rank[top[belong[v]]],rank[v],rank[top[belong[v]]]));
            v=fa[top[belong[v]]];
        }
    }
    ans=max(ans,maxquery(root[belong[u]],1,rank[top[belong[u]]],min(rank[u],rank[v]),max(rank[u],rank[v])));
    return ans;
}
int sumquery1(int u,int v)
{
    int ans=0;
    while(belong[u]!=belong[v])
    {
        if (deep[top[belong[u]]]>deep[top[belong[v]]])
        {
            ans+=sumquery(root[belong[u]],1,rank[top[belong[u]]],rank[u],rank[top[belong[u]]]);
            u=fa[top[belong[u]]];
        }
        else
        {
            ans+=sumquery(root[belong[v]],1,rank[top[belong[v]]],rank[v],rank[top[belong[v]]]);
            v=fa[top[belong[v]]];
        }
    }
    ans+=sumquery(root[belong[u]],1,rank[top[belong[u]]],min(rank[u],rank[v]),max(rank[u],rank[v]));
    return ans;
}
void work()
{
    dfs(1);top[belong[1]]=1;
    for (int i=1;i<=treenum;++i)
    {
        root[i]=++nodenum;
        build(root[i],1,rank[top[i]]);
    }
    for (int i=1;i<=n;++i)
    change(root[belong[i]],1,rank[top[belong[i]]],rank[i],w[i]);
}
int main()
{
    scanf("%d",&n);int x,y;
    for (int i=1;i<n;++i)
    {
        scanf("%d%d",&x,&y);
        add(x,y);add(y,x);
    }
    for (int i=1;i<=n;++i) scanf("%d",&w[i]);
    work();scanf("%d",&q);
    for (;q;--q)
    {
        scanf("%s",opt+1);scanf("%d%d",&x,&y);
        if (opt[1]=='C') change(root[belong[x]],1,rank[top[belong[x]]],rank[x],y);
        else if (opt[2]=='M') printf("%d\n",maxquery1(x,y));
        else printf("%d\n",sumquery1(x,y));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值