玲珑学院OJ 1129 喵哈哈村的战斗魔法师丶坏坏い月【暴力分块】

1129 - 喵哈哈村的战斗魔法师丶坏坏い月

Time Limit:3s Memory Limit:256MByte

Submissions:542Solved:124

DESCRIPTION

坏坏い月是月大叔的ID,他是一个掌握者772002种魔法的物理系战士,最擅长的技能就是搞事。今天他又要开始搞事了。

给你nn个数,你需要实现一下操作:

  1. l r v ,在[l,r]区间内找到第一个大于等于v的数,输出这个数的下标,如果找不到的话,请输出-1噢

  2. l r v,让[l,r]区间所有数增加v

INPUT
输入第一行包含一个正整数 t(1t100)t(1≤t≤100) ,表示有t组数据对于每组数据:第一行包含两个整数 n(1n100000)n(1≤n≤100000), q(1q100000)q(1≤q≤100000),表示数的个数,以及询问的个数。第二行包含 nn个整数 ai(1ai1000000000)ai(1≤ai≤1000000000)接下来q行,每行四个整数 opt(1opt2),l,r(1lrn),v(1v1000000000)opt(1≤opt≤2),l,r(1≤l≤r≤n),v(1≤v≤1000000000)
OUTPUT
对于每个询问,输出一行表示答案.
SAMPLE INPUT
1
5 3
1 2 3 4 5
1 1 2 3
2 1 2 3
1 1 2 3
SAMPLE OUTPUT
-1
1

思路:


问题其实是在问,区间【L,R】内,第一个大于等于v这个数的最左边的位子。

那么我们其实可以二分加线段树查询区间最大值去做。时间复杂度为O(n*(logn)^2),logn约等于17.平方后约等于300.


这里学一下暴力分块。暴力分块其实就是将整个数组分成sqrt(n)块,然后维护几个数组即可,对于整块整块的更新值的时候,我们额外开出一个数组维护整块整块的提升的总价值和就能很好的解决这类更新和查询问题,时间复杂度O(nsqrt(n)),sqrt(n)为300+,约等于300.两种方法差不多,差在常数,肯定二分要快一些。




#include<stdio.h>
#include<string.h>
#include<math.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long int
ll n,q,Len,tot;
ll a[150000],belong[150000];
ll L[500],R[500],maxn[500],Add[500];
void init()
{
    memset(Add,0,sizeof(Add));
    Len=(ll)sqrt(n);
    tot=n/Len;if(n%Len!=0)tot++;
    for(ll i=1;i<=n;i++)belong[i]=(i-1)/Len+1;
    for(ll i=1;i<=tot;i++)L[i]=(i-1)*Len+1,R[i]=i*Len;
    for(ll i=1;i<=n;i++)maxn[belong[i]]=max(maxn[belong[i]],a[i]);
}
void update(ll l,ll r,ll w)
{
    ll LL=belong[l];
    ll RR=belong[r];
    if(LL==RR)
    {
        for(ll i=l;i<=r;i++)
        {
            a[i]+=w;
            maxn[belong[i]]=max(maxn[belong[i]],a[i]+Add[belong[i]]);
        }
    }
    else
    {
        for(ll i=l;i<=R[LL];i++)
        {
            a[i]+=w;
            maxn[belong[i]]=max(maxn[belong[i]],a[i]+Add[belong[i]]);
        }
        for(ll i=L[RR];i<=r;i++)
        {
            a[i]+=w;
            maxn[belong[i]]=max(maxn[belong[i]],a[i]+Add[belong[i]]);
        }
        for(ll i=LL+1;i<=RR-1;i++)
        {
            Add[i]+=w;
            maxn[i]+=w;
        }
    }
}
ll query(ll l,ll r,ll w)
{
    ll LL=belong[l];
    ll RR=belong[r];
    if(LL==RR)
    {
        for(ll i=l;i<=r;i++)
        {
            if(a[i]+Add[belong[i]]>=w)return i;
        }
        return -1;
    }
    for(ll i=l;i<=R[LL];i++)
    {
        if(a[i]+Add[belong[i]]>=w)return i;
    }
    for(ll i=LL+1;i<=RR-1;i++)
    {
        if(maxn[i]>=w)
        {
            for(ll j=L[i];j<=R[i];j++)
            {
                if(a[j]+Add[belong[j]]>=w)return j;
            }
        }
    }
    for(ll i=L[RR];i<=r;i++)
    {
        if(a[i]+Add[belong[i]]>=w)return i;
    }
    return -1;
}
int main()
{
    ll t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%lld%lld",&n,&q);
        for(ll i=1;i<=n;i++)scanf("%lld",&a[i]);
        init();
        while(q--)
        {
            ll op,x,y,z;
            scanf("%lld%lld%lld%lld",&op,&x,&y,&z);
            if(op==1)
            {
                printf("%lld\n",query(x,y,z));
            }
            if(op==2)
            {
                update(x,y,z);
            }
        }
    }
}










评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值