POJ 3264:区间最值差 ← 分块

【问题描述】
题目来源:
http://poj.org/problem?id=3264
https://www.acwing.com/problem/content/1276/

在每天挤奶的时候,农民约翰的N头牛(1≤n≤50000)总是排成一列。有一天,约翰决定与他的牛们一起玩一个极限飞盘游戏。为了简单起见,他将从奶牛队列里面选一定范围内的奶牛来玩这个游戏。然而所有的牛对这个游戏都很感兴趣。农民约翰列出了Q份名单(1≤Q≤200000)和每个奶牛的高度(1≤高度≤1000000)。对于每一份名单,他想你帮助他确定在每份名单中高度最高的奶牛与高度最低的奶牛的高度差是多少。

【输入格式】
第一行为N(1≤N≤50000)和Q(1≤Q≤200000);
从第2行到第N+1行,每行一个数字,表示第i头牛的高度(1≤height≤1000000);
从第N+2行到第N+Q+1行,每行两个整数A和B(1≤A≤B≤N),表示从第A头牛到第B头牛的范围。

【输出格式】
从第一行到第Q行,每行一个整数,表示从第A头牛到第B头牛之间,最高牛与最矮牛的高度差。

【输入输出样例】
输入:
6 3
1
7
3
4
2
5
1 5
4 6
2 2

输出: 
6
3
0

【算法分析】
分块算法可以解决几乎所有区间更新和区间查询问题,但效率相对于线段树等数据结构要差一些。
分块算法可以维护一些线段树维护不了的内容(如线段树不能维护不满足区间可加、可减的信息)。
分块算法其实就是优化过后的暴力算法。
分块算法通常将长度为n的序列分成 sqrt(n) 块。

【算法代码】

#include<bits/stdc++.h>
using namespace std;

const int inf=0x3f3f3f3f;
const int maxn=50010;

int L[maxn],R[maxn];
int pos[maxn]; //存储第i个元素所在的块
int block_max[maxn],block_min[maxn];
int a[maxn];
int n,m;

void build() {
    int t=sqrt(n*1.0); //t为每块的元素个数。sqrt函数参数没有整数,所以乘以1.0
    int num=n/t; //分为num块
    if(n%num) num++;
    for(int i=1; i<=num; i++) { //每一块的左右区间端点序号
        L[i]=(i-1)*t+1;
        R[i]=i*t;
    }
    R[num]=n;

    for(int i=1; i<=n; i++) pos[i]=(i-1)/t+1;

    for(int i=1; i<=num; i++) { //求每块的最大值及最小值
        int segmin=inf,segmax=-inf;
        for(int j=L[i]; j<=R[i]; j++) {
            segmax=max(segmax,a[j]);
            segmin=min(segmin,a[j]);
        }
        block_max[i]=segmax;
        block_min[i]=segmin;
    }
}

int query(int le,int ri) {
    int segmin=inf,segmax=-inf;
    if(pos[le]==pos[ri]) {
        for(int i=le; i<=ri; i++) {
            segmax=max(segmax,a[i]);
            segmin=min(segmin,a[i]);
        }
        return segmax-segmin;
    } else {
        for(int i=le; i<=R[pos[le]]; i++) { //左端
            segmax=max(segmax,a[i]);
            segmin=min(segmin,a[i]);
        }
        for(int i=pos[le]+1; i<pos[ri]; i++) { //中间
            segmax=max(segmax,block_max[i]);
            segmin=min(segmin,block_min[i]);
        }
        for(int i=L[pos[ri]]; i<=ri; i++) { //右端
            segmax=max(segmax,a[i]);
            segmin=min(segmin,a[i]);
        }
    }
    return segmax-segmin;
}

int main() {
    int le,ri;
    while(~scanf("%d%d",&n,&m)) {
        for(int i=1; i<=n; i++) //下标从1开始
            scanf("%d",&a[i]);
        build();
        for(int j=1; j<=m; j++) {
            scanf("%d%d",&le,&ri);
            printf("%d\n",query(le,ri));
        }
    }
    return 0;
}


/*
in:
6 3
1
7
3
4
2
5
1 5
4 6
2 2

out:
6
3
0
*/


【参考文献】
http://poj.org/problem?id=3264
https://www.acwing.com/problem/content/1276/

 

猜你喜欢

转载自blog.csdn.net/hnjzsyjyj/article/details/120788758