去掉最大值,最小值的区间和——线段树

A - 一棵简单的线段树

 

人生已如此艰难,让我们活得轻松一点.

给你一个数组 A[1..n]A[1..n],初始时每个元素都为零.

我会请你帮我对数组完成一些操作.

第一种可能,我会给你两个数 pp 和 xx1pn1≤p≤n), 
请你帮我把数组的第 pp 个元素替换为 xx, 
即 A[p]xA[p]←x.

第二种可能,我会给你两个数 LL 和 RR1L<Rn1≤L<R≤n), 
请你告诉我 A[L],A[L+1],,A[R]A[L],A[L+1],…,A[R] 
这几个数中去掉一个最大值和一个最小值后剩下的数的和是多少.

好了,现在锅都丢给你了,我可以活得轻松一点了.

Input

输入第一行为一个整数 n (2n106)n (2≤n≤106),表示数组的大小.

第二行有一个整数 m (1m106)m (1≤m≤106),表示我需要你帮我完成的任务的个数.

第 33 到 m+2m+2 行每行有 3 个整数 o x yo x y
如果 o=0o=0,代表我需要使 A[x]yA[x]←y, 
此时 1xn, |y|1091≤x≤n, |y|≤109
如果 o=1o=1,代表我想知道 A[x],A[x+1],,A[y]A[x],A[x+1],…,A[y] 
去掉一个最大值和一个最小值后剩下的数的和为多少, 
此时 1x<yn1≤x<y≤n.

Output

对每个 o=1o=1 的任务, 
输出只有一个整数的一行, 
该整数表示区间 [x,y][x,y] 中的数 
去掉一个最大值和一个最小值后剩下的数的和.

Sample Input
5
5
0 2 1
0 4 -2
1 1 5
0 1 5
1 1 4
Sample Output
0
1

线段树的核心就是把一个区间分成两半再操作

三个数组,一个存区间和,一个存区间最大值,一个存区间最小值

坑点:区间和会爆int。。。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int INF=0x7f7f7f7f;
long long btree[4000005];
int bmax[4000005];
int bmin[4000005];
int n,t;
int l,r;
int o,x,y;
void buildtree(int l,int r,int rt)//建树,rt为当前位置
{
    if(l==r)
    {
        btree[rt]=0;
        return ;
    }
    int mid=l+(r-l)/2;
    buildtree(l,mid,rt*2);
    buildtree(mid+1,r,rt*2+1);
    btree[rt]=btree[rt*2]+btree[rt*2+1];
}
void buildmaxtree(int l,int r,int rt)
{
    if(l==r)
    {
        bmax[rt]=0;
        return ;
    }
    int mid=l+(r-l)/2;
    buildmaxtree(l,mid,rt*2);
    buildmaxtree(mid+1,r,rt*2+1);
    bmax[rt]=max(bmax[rt*2],bmax[rt*2+1]);

}
void buildmintree(int l,int r,int rt)
{
    if(l==r)
    {
        bmax[rt]=0;
        return ;
    }
    int mid=l+(r-l)/2;
    buildmintree(l,mid,rt*2);
    buildmintree(mid+1,r,rt*2+1);
    bmax[rt]=max(bmax[rt*2],bmax[rt*2+1]);

}
void modify(int l,int r,int rt,int p,int x)//单点修改值,p为值的下标,x为修改的目标值
{
    if(l==r)
    {
        btree[rt]=x;
        bmax[rt]=x;
        bmin[rt]=x;
        return ;
    }
    int mid=l+(r-l)/2;
    if(p<=mid) modify(l,mid,rt*2,p,x);
    else modify(mid+1,r,rt*2+1,p,x);
    btree[rt]=btree[rt*2]+btree[rt*2+1];
    bmax[rt]=max(bmax[rt*2],bmax[rt*2+1]);
    bmin[rt]=min(bmin[rt*2],bmin[rt*2+1]);
}
long long findsum(int l,int r,int rt,int ll,int rr)//查询区间的和,ll区间左端,rr区间右端
{
    if(ll<=l&&r<=rr) return btree[rt];
    int mid=l+(r-l)/2;
    long long ans=0;
    if(ll<=mid) ans+=findsum(l,mid,rt*2,ll,rr);
    if(rr>mid) ans+=findsum(mid+1,r,rt*2+1,ll,rr);
    return ans;
}
int findmax(int l,int r,int rt,int ll,int rr)
{
    if(ll<=l&&r<=rr) return bmax[rt];
    int mid=l+(r-l)/2;
    int ans=-INF;
    if(ll<=mid) ans=max(ans,findmax(l,mid,rt*2,ll,rr));
    if(rr>mid) ans=max(ans,findmax(mid+1,r,rt*2+1,ll,rr));
    return ans;
}
int findmin(int l,int r,int rt,int ll,int rr)
{
    if(ll<=l&&r<=rr) return bmin[rt];
    int mid=l+(r-l)/2;
    int ans=INF;
    if(ll<=mid) ans=min(ans,findmin(l,mid,rt*2,ll,rr));
    if(rr>mid) ans=min(ans,findmin(mid+1,r,rt*2+1,ll,rr));
    return ans;
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        memset(bmax,0,sizeof bmax);
        memset(bmin,0,sizeof bmin);
        memset(btree,0,sizeof btree);
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d%d%d",&o,&x,&y);
            if(o==0)
            {
                modify(1,n,1,x,y);
            }
            else
            {
                printf("%lld\n",findsum(1,n,1,x,y)-findmax(1,n,1,x,y)-findmin(1,n,1,x,y));
            }
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/codetypeman/article/details/80982788
今日推荐