线段树水题区间最大值,最小值,求和CDOJ1921

饺子发现身边危机四伏!有nn个人站在一维平面上,每个人的坐标为Ai,能量值为Bi。饺子有cc种方法可以推测出有多少人想吃饺子,请你求出每种方法可以得出的想吃饺子的人数。

Input

第一行两个值 n(n≤105),c(c≤10)n
接下来n行每行两个值Ai,Bi(1≤Ai≤109,1≤Bi≤104,Ai≤Aj)  if Q__Q题目链接 接下来c行,每行包含三个数K,function
其中 
K可能为gt或者lt,代表大于或者小于 
function可能为minmax或者avg,代表最小,最大或者平均 
length是一个整数(1≤length≤109)
K,function,length的意思为对于第ii号人,如果Bi K于[Ai−length,Ai)范围内的所有人能量值的function值,说明该人想吃饺子,特别的如果范围内一个人也没有,则这个人不想吃饺子

Output

对于每种方法,输出想吃饺子的人数

Sample Input

10 2
60 30
120 28
180 35
240 34
300 40
360 31
420 28
480 2
540 42
600 30
gt avg 7200
lt min 300

Sample Output

4
2

需要将区间[Ai−length,Ai) 处理到线段树的1--n区间上,由于A是升序排列的,所以二分查找到区间左端的下标

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=1e5+7;
long long btree[400005];
int bmax[400005];
int bmin[400005];
int n,c;
int a[maxn];
int b[maxn];
void buildtree(int l,int r,int rt)//建树,rt为当前位置
{
    if(l==r)
    {
        btree[rt]=b[l];
        bmin[rt]=b[l];
        bmax[rt]=b[l];
        return ;
    }
    int mid=(l+r)>>1;;
    buildtree(l,mid,rt*2);
    buildtree(mid+1,r,rt*2+1);
    btree[rt]=btree[rt*2]+btree[rt*2+1];
    bmax[rt]=max(bmax[rt<<1],bmax[rt<<1|1]);
    bmin[rt]=min(bmin[rt<<1],bmin[rt<<1|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()
{
    int len;
    char k[10];
    char fun[10];

    scanf("%d%d",&n,&c);
    for(int i=1;i<=n;++i)
        scanf("%d%d",&a[i],&b[i]);
    buildtree(1,n,1);
    while(c--)
    {
        int ans=0;
        scanf("%s%s%d",k,fun,&len);
        for(int i=1;i<=n;++i)
        {
            int le=lower_bound(a+1,a+1+n,a[i]-len)-a;
            int ri=lower_bound(a+1,a+1+n,a[i])-a;
            ri--;
            if(le>ri) continue;
            if(fun[1]=='i')
            {
                int mmin=findmin(1,n,1,le,ri);
                //cout<<mmin<<endl;
                if(k[0]=='g')
                {
                    if(b[i]>mmin) ans++;
                }
                else
                {
                    if(b[i]<mmin)
                    {

                        ans++;
                    }
                }
            }
            else if(fun[1]=='v')
            {
                int sum=findsum(1,n,1,le,ri);
                int cnt=ri-le+1;
                float ave=sum*1.0/cnt;
                if(k[0]=='g')
                {
                    if(b[i]>ave) ans++;
                }
                else
                {
                    if(b[i]<ave) ans++;
                }
            }
            else
            {
                int mmax=findmax(1,n,1,le,ri);
                if(k[0]=='g')
                {
                    if(b[i]>mmax) ans++;
                }
                else
                {
                    if(b[i]<mmax) ans++;
                }
            }
        }
        cout<<ans<<endl;

    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值