博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

洛谷 P1816 忠诚 解题报告

题目描述

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

输入输出格式

输入格式:

输入中第一行有两个数m,n表示有m(m<=100000)笔账,n表示有n个问题,n<=100000。

第二行为m个数,分别是账目的钱数

后面n行分别是n个问题,每行有2个数字说明开始结束的账目编号。

输出格式:

输出文件中为每个问题的答案。具体查看样例。

输入输出样例

输入样例#1:
10 3
1 2 3 4 5 6 7 8 9 10
2 7
3 9
1 10
输出样例#1:
2 3 1

分块:

(这个题也可以用线段树做(同样非常简单),但练习分块所以整理一下。)

首先,将长度为n的序列分割成√n个块。维护每一个块内的最小值

查询一段区间的时候,先跑两段暴力:

1、x到x所属区间的右端点。

2、y所属区间的左端点到y

再枚举x和y之间的块已经维护出来的最小值


代码如下:

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

inline void read(ll &a)
{
    a=0;int c=getchar();
    while(c>'9'||c<'0') c=getchar();
    while(c>='0'&&c<='9') a=(a<<3)+(a<<1)+c-48,c=getchar();
}

const int N=1e5+5;
ll n,m,minx[N],block,a[N],bl[N],l,r;

inline ll query(ll l,ll r)
{
    ll ans=0x7fffff;
    for(int i=l;i<=min(bl[l]*block,r);i++) ans=min(a[i],ans);
    if(bl[l]!=bl[r])
    {
        for(int i=(bl[r]-1)*block+1;i<=r;i++) ans=min(a[i],ans);
    }
    for(int i=bl[l]+1;i<bl[r];i++) ans=min(minx[i],ans);
    return ans;
}


int main()
{
    memset(minx,0x7f,sizeof(minx));
    read(n);read(m);
    for(int i=1;i<=n;i++) read(a[i]);
    block=sqrt(n);
    for(int i=1;i<=n;i++)
    {
        bl[i]=(i-1)/block+1;
        minx[bl[i]]=min(minx[bl[i]],a[i]);
    }
    while(m--)
    {
        read(l);read(r);
        cout<<query(l,r)<<' ';
    }
}
View Code

 

 

 

 

posted @ 2018-12-08 20:05  楚泫  阅读(202)  评论(0编辑  收藏  举报