洛谷 P1816:忠诚 ← 序列分块算法(区间查询)

【题目来源】
https://www.luogu.com.cn/problem/P1816

【题目描述】
老管家是一个聪明能干的人。他为财主工作了整整 10 年。财主为了让自己账目更加清楚,要求管家每天记 k 次账。由于管家聪明能干,因而管家总是让财主十分满意。
但是由于一些人的挑拨,财主还是对管家产生了怀疑。于是他决定用一种特别的方法来判断管家的忠诚。他把每次的账目按 1,2,3,… 编号,然后不定时地问管家这样的问题:在 a 到 b 号账中最少的一笔是多少?
为了让管家没时间作假,他总是一次问多个问题。

【输入格式】
第一行输入两个数 m,n,表示有 m 笔账和 n 个问题。
第二行输入 m 个数,分别表示账目的钱数 xi​。
接下来 n 行分别输入 n 个问题,每行 2 个数字,分别表示开始的账目编号 a 和结束的账目编号 b。

【输出格式】
第一行输出每个问题的答案,每个答案中间以一个空格分隔。

【输入样例】
10 3
1 2 3 4 5 6 7 8 9 10
2 7
3 9
1 10

【输出样例】
2 3 1

【数据范围】
对于 100% 的数据,1≤m≤10^5,1≤n≤10^5,0≤xi≤10^5。

【算法分析】
● “序列分块”算法的基本要素及 build() 函数的构建细节:https://blog.csdn.net/hnjzsyjyj/article/details/138955263

● 由于“洛谷 P1816:忠诚”只有查询没有修改,所以分块结构建好后就不需要再更新。所以,“洛谷 P1816:忠诚”还可以用 ST 表(稀疏表)来做,预处理 O(nlog n),查询 O(1),比分块更快。
ST算法详见:https://blog.csdn.net/hnjzsyjyj/article/details/152514708

【算法代码】

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

const int N=1e5+5;
const int INF=INT_MAX;
int a[N],le[N],ri[N];
int block_min[N];
int pos[N];
int n,m;

void build() {
    int block=sqrt(n);
    int cnt=(n+block-1)/block;
    for(int i=0; i<cnt; i++) {
        le[i]=i*block;
        ri[i]=(i+1)*block-1;
    }
    ri[cnt-1]=n-1;
    for(int i=0; i<n; i++) pos[i]=i/block;

    //Calculate the min value for each block
    for(int i=0; i<cnt; i++) {
        block_min[i]=INF;
        for(int j=le[i]; j<=ri[i]; j++) {
            block_min[i]=min(block_min[i],a[j]);
        }
    }
}

int query(int st,int ed) {
    int res=INF;

    //st and ed are in the same block
    if(pos[st]==pos[ed]) {
        for(int i=st; i<=ed; i++) res=min(res,a[i]);
        return res;
    }

    /*Process the scattered blocks on the left
     (from st to the right boundary of the block
     where st is located)*/
    for(int i=st; i<=ri[pos[st]]; i++) {
        res=min(res,a[i]);
    }

    //Deal with the entire middle block
    for(int i=pos[st]+1; i<pos[ed]; i++) {
        res=min(res,block_min[i]);
    }

    /*Process the scattered block on the right
     (from the left boundary of the block
     where ed is located to ed)*/
    for(int i=le[pos[ed]]; i<=ed; i++) {
        res=min(res,a[i]);
    }

    return res;
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin>>n>>m;
    for(int i=0; i<n; i++) cin>>a[i];

    build();

    while(m--) {
        int le,ri;
        cin>>le>>ri;
        le--;
        ri--;
        cout<<query(le,ri)<<" ";
    }

    return 0;
}


/*
in:
10 3
1 2 3 4 5 6 7 8 9 10
2 7
3 9
1 10

out:
2 3 1
*/





【参考文献】
https://blog.csdn.net/hnjzsyjyj/article/details/138903837
https://blog.csdn.net/hnjzsyjyj/article/details/152514708
https://blog.csdn.net/hnjzsyjyj/article/details/138863063

posted @ 2026-07-15 13:21  Triwa  阅读(10)  评论(0)    收藏  举报