HDU 6315 Naive Operations (线段树)

题目链接

给定一个数组b和一个空的数组a,要求支持两种操作:将a在从l到r上每项都加1,查询从l到r区间上\left \lfloor \frac{a_{i}}{b_{i}} \right \rfloor之和。

由于每一个bi都是不同的,直接维护ai/bi很困难。

观察要求查询的是区间内ai/bi向下取整之和,且ai初始为0。显然ai每增加bi次,才能使结果增加1。于是我们可以把这个问题转化成,查询的区间内结果增加了多少次,也就是说,查询的区间内ai的值每增加bi一次结果就会增加1,我们需要找出区间内所有的ai增加到bi一共有多少次。

开两个线段树,其中一个线段树维护区间最小值,初始时保存的是各个bi的值。每当要求给a在l到r上增加1的时候,我们处理的方法是通过这个线段树给l到r这个区间减1。

每次减完以后,我们看一下有没有位置减到了0。如果有,则说明这个位置上ai增加到了一次bi,最终的结果需要增加1。这个时候我们找出这些位置,在另一个线段树上将这个位置的值加1,这第二个线段树维护区间和,保存的就是各个区间的答案值。

而当查询的时候,就只需要在第二个线段树上查询区间和即为答案。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<vector>
#include<iostream>
#include<algorithm>
#define maxn 100050
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;

int n,m,x,y,b[maxn];
int tree[maxn<<2],lazy[maxn<<2];
int sum[maxn<<2];
char s[15];

void build(int root,int l,int r)
{
    lazy[root]=sum[root]=0;
    if(l==r)
    {
        tree[root]=b[l];
        return;
    }
    int mid=(l+r)>>1;
    build(root*2,l,mid);
    build(root*2+1,mid+1,r);
    tree[root]=min(tree[root*2],tree[root*2+1]);
}

void addlazy(int root,int l,int r)
{
    if(lazy[root]!=0)
    {
        lazy[root*2]+=lazy[root];
        lazy[root*2+1]+=lazy[root];
        tree[root*2]+=lazy[root];
        tree[root*2+1]+=lazy[root];
        lazy[root]=0;
    }
}

void add(int root,int l,int r,int aiml,int aimr)
{
    if(l>aimr||r<aiml)return;
    if(l>=aiml&&r<=aimr)
    {
        lazy[root]--;
        tree[root]--;
        return;
    }
    addlazy(root,l,r);
    int mid=(l+r)>>1;
    add(root*2,l,mid,aiml,aimr);
    add(root*2+1,mid+1,r,aiml,aimr);
    tree[root]=min(tree[root*2],tree[root*2+1]);
}

void addsum(int root,int l,int r,int aim)
{
    if(l>aim||r<aim)return;
    if(l==r&&r==aim)
    {
        sum[root]++;
        return;
    }
    int mid=(l+r)>>1;
    addsum(root*2,l,mid,aim);
    addsum(root*2+1,mid+1,r,aim);
    sum[root]=sum[root*2]+sum[root*2+1];
}

void update(int root,int l,int r)
{
    if(tree[root]>0)return;
    if(l==r)
    {
        tree[root]=b[l];
        addsum(1,1,n,l);
        return;
    }
    addlazy(root,l,r);
    int mid=(l+r)>>1;
    update(root*2,l,mid);
    update(root*2+1,mid+1,r);
    tree[root]=min(tree[root*2],tree[root*2+1]);
}

int query(int root,int l,int r,int aiml,int aimr)
{
    if(l>aimr||r<aiml)return 0;
    if(l>=aiml&&r<=aimr)return sum[root];
    int mid=(l+r)>>1;
    return query(root*2,l,mid,aiml,aimr)+query(root*2+1,mid+1,r,aiml,aimr);
}

int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(int i=1;i<=n;i++)
            scanf("%d",&b[i]);
        build(1,1,n);
        for(int i=0;i<m;i++)
        {
            scanf("%s%d%d",s,&x,&y);
            if(s[0]=='a')
            {
                add(1,1,n,x,y);
                if(tree[1]==0)
                    update(1,1,n);
            }
            else
            {
                int ans=query(1,1,n,x,y);
                printf("%d\n",ans);
            }
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值