bzoj 1036 Codevs 2460 树的统计 [树链剖分]

本文介绍了一种关于树结构的数据处理算法,通过树链剖分和线段树维护的方法,实现对树上节点权值的高效变更及路径查询。具体包括节点权值的最大值和总和查询等功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1036: [ZJOI2008]树的统计Count

Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 13024 Solved: 5253

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


树链剖分裸题,用线段树维护。。。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<climits>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
const int INF=0x3f3f3f3f;
const int maxn = 30005;
struct Edge
{
    int to,next;
}edge[maxn<<1];
int head[maxn];
int maxedge;
inline void addedge(int u,int v)
{
    edge[++maxedge] = (Edge) { v,head[u] };
    head[u] = maxedge;
    edge[++maxedge] = (Edge) { u,head[v] };
    head[v] = maxedge;
}
int a[maxn],id[maxn],rid[maxn],top[maxn];
int depth[maxn],son[maxn],size[maxn],fa[maxn];
int sum[maxn<<2],mx[maxn<<2]; // seg part
int maxnode;
int n;
void dfs1(int u,int father,int deep)
{
    fa[u]=father; depth[u]=deep; size[u]=1;
    for(int i=head[u];~i;i=edge[i].next)
    {
        int v = edge[i].to;
        if(v == father) continue;
        dfs1(v,u,deep+1);
        size[u] += size[v];
        if(!son[u] || size[v]>size[son[u]]) son[u]=v;
    }
}
void dfs2(int u,int tp)
{
    top[u]=tp; id[u]=++maxnode; rid[maxnode]=u;
    if(son[u]) dfs2(son[u],tp);
    for(int i=head[u];~i;i=edge[i].next)
    {
        int v = edge[i].to;
        if(v==fa[u] || v==son[u]) continue;
        dfs2(v,v);
    }
}
void init()
{
    scanf("%d",&n);
    memset(head,-1,sizeof(head));
    maxedge = -1;
    for(int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        addedge(x,y);
    }
    for(int i=1;i<=n;i++) scanf("%d",a+i);
    memset(son,0,sizeof(son));
    memset(size,0,sizeof(size));
    memset(depth,0,sizeof(depth));
    memset(top,0,sizeof(top));
    dfs1(1,-1,1);
    dfs2(1,1);
}
inline void update(int root)
{
    sum[root]=sum[root<<1]+sum[root<<1|1];
    mx[root]=max(mx[root<<1],mx[root<<1|1]);
}
void build(int root,int l,int r)
{
    if(l==r)
    {
        sum[root]=mx[root]=a[rid[l]];
        return;
    }
    int m=(l+r)>>1;
    build(root<<1,l,m);
    build(root<<1|1,m+1,r);
    update(root);
}
void modify(int root,int l,int r,int pos,int val)
{
    if(l==r)
    {
        sum[root]=mx[root]=val;
        return ;
    }
    int m=(l+r)>>1;
    if(pos<=m) modify(root<<1,l,m,pos,val);
    else modify(root<<1|1,m+1,r,pos,val);
    update(root);
}
int querysum(int root,int l,int r,int x,int y)
{
    if(x<=l && r<=y) return sum[root];
    int m=(l+r)>>1;
    int ret=0;
    if(x<=m && l<=y) ret += querysum(root<<1,l,m,x,y);
    if(y>=m+1 && r>=x) ret += querysum(root<<1|1,m+1,r,x,y);
    return ret;
}
int querymax(int root,int l,int r,int x,int y)
{
    if(x<=l && r<=y) return mx[root];
    int m=(l+r)>>1;
    int ret=-INF;
    if(x<=m && l<=y) smax(ret , querymax(root<<1,l,m,x,y));
    if(y>=m+1 && r>=x) smax(ret , querymax(root<<1|1,m+1,r,x,y));
    return ret;
}
int FindSum(int u,int v)
{
    int ret=0;
    int t1=top[u],t2=top[v];
    while(t1^t2)
    {
        if(depth[t1]<depth[t2]) swap(t1,t2),swap(u,v);
        ret += querysum(1,1,maxnode,id[t1],id[u]);
        u=fa[t1];
        t1=top[u];
    }
    if(depth[u]<depth[v]) swap(u,v);
    ret += querysum(1,1,maxnode,id[v],id[u]);
    return ret;
}
int FindMax(int u,int v)
{
    int ret=-INF;
    int t1=top[u],t2=top[v];
    while(t1^t2)
    {
        if(depth[t1]<depth[t2]) swap(t1,t2),swap(u,v);
        smax(ret,querymax(1,1,maxnode,id[t1],id[u]));
        u=fa[t1];
        t1=top[u];
    }
    if(depth[u]<depth[v]) swap(u,v);
    return max(ret,querymax(1,1,maxnode,id[v],id[u]));
}
void work()
{
    build(1,1,maxnode);
    int q;
    scanf("%d",&q);
    int x,y;
    char s[10];
    while(q--)
    {
        scanf("%s%d%d",s,&x,&y);
        if(s[0]=='C') modify(1,1,maxnode,id[x],y);
        else if(s[1]=='S') printf("%d\n",FindSum(x,y));
             else printf("%d\n",FindMax(x,y));
    }
}
int main()
{
    freopen("count.in","r",stdin);
    freopen("count.out","w",stdout);
    init();
    work();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值