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

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

Time Limit:3s Memory Limit:256MByte

Submissions:315Solved:71

DESCRIPTION

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

给你n

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

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

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

INPUT
输入第一行包含一个正整数t(1t100)

,表示有t组数据 对于每组数据: 第一行包含两个整数n(1n100000),q(1q100000),表示数的个数,以及询问的个数。 第二行包含n个整数 ai(1ai1000000000) 接下来q行,每行四个整数opt(1opt2),l,r(1lrn),v(1v1000000000)

 
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
 
博客本来准备不更新了,今天看到这个题目 - -,想起如果用树维护又是树套树啥的,麻烦死了,结果看到题解->分块! 
看了分块思想之后,感觉好巧妙啊,时间复杂度 O(m*sqrt(n)),这么难的数据结构题寥寥数十行就解决了,遂决定补题解!
 
官方题解:
H 喵哈哈村的战斗魔法师丶坏坏い月
常见的数据结构中,我们选择使用分块来处理这道题。
我们将n个数,分成sqrt(n)块,每块里面的元素最多有sqrt(n)个元素。对于每一个块,我们维护Upd[i]表示这个块整个增加了多少,Max[i]表示这个块内的最大值是多少。
对于查询和更新,如果完全囊括了块的话,我们就只对Upd[i]和Max[i]进行更新,否则的话,我们就暴力更新这个块的元素即可。
总体复杂度O(n*根号n)
 
Upd相当于一个标记,只起到记录的作用,两端暴力更新~
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long LL;
const int N = 100000;
int n,m;
LL a[N],Max[N],Upd[N];
int pos[N];
int block = 0;
void reset(int x)
{
    int l=(x-1)*block+1,r=min(x*block,n);
    for(int i=l; i<=r; i++)
        Max[x]=max(Max[x],a[i]);
}

void update(int x,int y,LL v)
{
    if(pos[x]==pos[y])
    {
        for(int i=x; i<=y; i++)a[i]=a[i]+v;
    }
    else
    {
        for(int i=x; i<=pos[x]*block; i++)a[i]=a[i]+v;
        for(int i=(pos[y]-1)*block+1; i<=y; i++)a[i]=a[i]+v;
    }
    reset(pos[x]);
    reset(pos[y]);
    for(int i=pos[x]+1; i<pos[y]; i++)
        Upd[i]+=v;
}

int findidx(int x,LL v)
{
    int l=(x-1)*block+1,r=min(x*block,n);
    for(int i=l; i<=r; i++)
    {
        if(a[i]+Upd[x]>=v) return i;
    }
}
int query(int x,int y,LL v)
{
    if(pos[x]==pos[y])
    {
        for(int i=x; i<=y; i++)if(a[i]+Upd[pos[i]]>=v) return i;
    }
    else
    {
        ///暴力找左边
        for(int i=x; i<=pos[x]*block; i++)
            if(a[i]+Upd[pos[i]]>=v) return i;
        ///分块找中间
        for(int i=pos[x]+1; i<pos[y]; i++)
            if(Max[i]+Upd[i]>=v)
            {
                return findidx(i,v);
            }
        ///暴力找右边
        for(int i=(pos[y]-1)*block+1; i<=y; i++)
            if(a[i]+Upd[pos[i]]>=v) return i;
    }

    return -1;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(Max,0,sizeof(Max));
        memset(Upd,0,sizeof(Upd));
        scanf("%d%d",&n,&m);
        block = int(sqrt(n));
        for(int i=1; i<=n; i++)
        {
            scanf("%lld",&a[i]);
            pos[i]=(i-1)/block+1; ///记录每个元素属于哪个块
        }
        int k; ///块的个数
        if(n%block)k=n/block+1;
        else k=n/block;
        for(int i=1; i<=k; i++)
        {
            reset(i);
        }
        while(m--)
        {
            int opt,l,r;
            LL v;
            scanf("%d%d%d%lld",&opt,&l,&r,&v);
            if(opt==1)
            {
                printf("%d\n",query(l,r,v));
            }
            else
            {
                update(l,r,v);
            }
        }
    }
    return 0;
}

 

posted @ 2017-05-30 22:29  樱花庄的龙之介大人  阅读(279)  评论(0编辑  收藏  举报