nefu 1328 区间求和(主席树+离线处理)

区间求和

Problem:1328
Time Limit:2000ms
Memory Limit:165535K

Description

有一个序列包含n个正整数,现在有m次询问,每次询问为:求(L,R)的区间中小于等于K的数的和?

Input

输入包含多组数据。每组数据,第一行为n,表示这个整数序列长为n(1<=n<=1e5)。第二行输入整数序列x1,x2,……,xn(1<=xi<=1e9)。第三行输入m(1<=m<=1e5)。接下来m行,每行输入L,R,K(1<=L<=R<=n,0<=K<=1e9)。

Output

每组数据首先输出“Case #x:”,x从1开始,然后m行输出每次询问的结果,具体格式请参考样例。

Sample Input

6
2 5 3 4 7 6
3
2 5 4
3 6 5
1 4 3

Sample Output

Case #1:
7
7
5

Hint

Source

CJJ
题意:

中文题。

思路:

一开始想这道题,想到了莫队。然后到后来调莫队的时候发现要想调对,几乎就是暴力了。果断弃题,根本没有往主席树上想。QAQ

如果是用主席树写的话,因为K在查询是未知的,无法确定小于等于K的值都在哪一棵树上,所以需要离线处理。我们把需要插入的数和询问的区间和K值都记录下来,并按照从小到大排序。如果遇到了数,我们就插入这个值,如果遇到了询问,我们就访问最近的那棵树,访问l到r区间。最后按照询问的顺序输出就行了。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=1e5+10;
struct data
{
    int v,id,f,l,r;
}q[2*maxn];
bool cmp(data a,data b)
{
    if(a.v!=b.v)
        return a.v<b.v;
    if(a.f!=b.f)
        return a.f<b.f;
    return a.id<b.id;
}
int root[maxn],cnt,a[maxn];
long long ans[maxn];
struct node
{
    int l,r,ls,rs;
    long long sum;
}tr[maxn*30];
int build(int l,int r)
{
    int now=++cnt;
    tr[now].l=l,tr[now].r=r;
    tr[now].sum=0;
    if(l==r) return now;
    int mid=(l+r)/2;
    tr[now].ls=build(l,mid);
    tr[now].rs=build(mid+1,r);
    return now;
}
int update(int pre,int pos,int c)
{
    int now=++cnt;
    tr[now]=tr[pre];
    tr[now].sum+=(long long)c;
    int l=tr[pre].l,r=tr[pre].r;
    if(l==r) return now;
    int mid=(l+r)/2;
    if(pos<=mid) tr[now].ls=update(tr[pre].ls,pos,c);
    else tr[now].rs=update(tr[pre].rs,pos,c);
    return now;
}
long long query(int now,int x,int y)
{
    long long ans=0;
    if(now<=0) return 0;
    int l=tr[now].l,r=tr[now].r;
    if(x<=l&&r<=y)
        return tr[now].sum;
    int mid=(l+r)/2;
    if(x<=mid) ans+=query(tr[now].ls,x,y);
    if(y>mid) ans+=query(tr[now].rs,x,y);
    return ans;
}
int main()
{
    int n,cas=1,m;
    while(~scanf("%d",&n))
    {
        memset(q,0,sizeof(q));
        cnt=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            q[i].id=i,q[i].v=a[i];
            q[i].f=0;
        }
        scanf("%d",&m);
        for(int i=n+1;i<=n+m;i++)
        {
            scanf("%d%d%d",&q[i].l,&q[i].r,&q[i].v);
            q[i].f=1;
            q[i].id=i-n;
        }
        sort(q+1,q+1+m+n,cmp);
        root[0]=build(1,n);
        int num=0;
        for(int i=1;i<=n+m;i++)
        {
            if(q[i].f==0)
            {
                num++;
                root[num]=update(root[num-1],q[i].id,q[i].v);
            }
            else
            {
                long long anss=query(root[num],q[i].l,q[i].r);
                ans[q[i].id]=anss;
            }
        }
        printf("Case #%d:\n",cas++);
        for(int i=1;i<=m;i++)
        {
            printf("%lld\n",ans[i]);
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值