GYM 100827 I.Salary Inequity(线段树)

本文介绍了一种解决有向树中子树的最大值与最小值差查询及子树权重更新的问题方法。通过预处理得到每个节点的深度优先搜索序列位置,利用线段树进行区间查询和更新操作。

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

Description
一棵有向树,1是根,每个点有点权,两种操作:
Q u:查询u节点为根节点的子树中权值最大值与最小值的差
R u x:给以u节点为根节点的子树所有点点权加上x
Input
第一行一整数T表示用例组数,每组用例首先输入一整数n表示点数,之后n-1个数,第i个数表示i+1点的父亲节点,之后n个数c[i]表示每个点的点权,然后输入一整数q表示操作数,最后q行每行一种操作
(2<=n<=1e6,1<=m<=1e4,1<=c[i]<=1e3,1<=x<=1e3)
Output
对于每次查询操作,输出结果
Sample Input
1
5
1 1 2 2
10 6 8 4 5
7
Q 2
Q 3
R 4 2
Q 2
Q 1
R 2 4
Q 1
Sample Output
2
0
1
5
2
Solution
求出每个点的dfs序后,两种操作分别变成查询区间最大值最小值以及区间更新,线段树即可
Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
#define INF 0x3f3f3f3f
#define maxn 1111111
int T,n,q,ll[maxn],rr[maxn],index;
vector<int>g[maxn];
void dfs(int u)
{
    ll[u]=++index;
    for(int i=0;i<g[u].size();i++)dfs(g[u][i]);
    rr[u]=index;
}
#define ls (t<<1)
#define rs ((t<<1)|1)
int Max[maxn<<2],Min[maxn<<2],Plus[maxn<<2];
void build(int l,int r,int t)
{
    Max[t]=Min[t]=Plus[t]=0;
    if(l==r)return ;
    int mid=(l+r)>>1;
    build(l,mid,ls),build(mid+1,r,rs);
}
void push_up(int t)
{
    Max[t]=max(Max[ls],Max[rs]);
    Min[t]=min(Min[ls],Min[rs]);
}
void push_down(int t)
{
    if(Plus[t])
    {
        int tt=Plus[t];
        Plus[t]=0;
        Max[ls]+=tt,Min[ls]+=tt,Plus[ls]+=tt;
        Max[rs]+=tt,Min[rs]+=tt,Plus[rs]+=tt;
    }
}
void update(int L,int R,int l,int r,int t,int v)
{
    if(L<=l&&r<=R)
    {
        Min[t]+=v,Max[t]+=v,Plus[t]+=v;
        return ;
    }
    push_down(t);
    int mid=(l+r)>>1;
    if(L<=mid)update(L,R,l,mid,ls,v);
    if(R>mid) update(L,R,mid+1,r,rs,v);
    push_up(t);
}
int query_max(int L,int R,int l,int r,int t)
{
    if(L<=l&&r<=R)return Max[t];
    push_down(t);
    int ans=0;
    int mid=(l+r)>>1;
    if(L<=mid)ans=max(ans,query_max(L,R,l,mid,ls));
    if(R>mid)ans=max(ans,query_max(L,R,mid+1,r,rs));
    return ans;
}
int query_min(int L,int R,int l,int r,int t)
{
    if(L<=l&&r<=R)return Min[t];
    push_down(t);
    int ans=INF;
    int mid=(l+r)>>1;
    if(L<=mid)ans=min(ans,query_min(L,R,l,mid,ls));
    if(R>mid)ans=min(ans,query_min(L,R,mid+1,r,rs));
    return ans;
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        index=0;
        for(int i=1;i<=n;i++)g[i].clear();
        for(int i=1;i<n;i++)
        {
            int fa;
            scanf("%d",&fa);
            g[fa].push_back(i+1);
        }
        dfs(1);
        build(1,n,1);
        for(int i=1;i<=n;i++)
        {
            int c;
            scanf("%d",&c);
            update(ll[i],ll[i],1,n,1,c);
        }
        scanf("%d",&q);
        while(q--)
        {
            char op[3];
            int x,c;
            scanf("%s",op);
            if(op[0]=='Q')
            {
                scanf("%d",&x);
                printf("%d\n",query_max(ll[x],rr[x],1,n,1)-query_min(ll[x],rr[x],1,n,1));
            }
            else
            {
                scanf("%d%d",&x,&c);
                update(ll[x],rr[x],1,n,1,c);
            }
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值