首页 > 代码库 > 洛谷——RMQ

洛谷——RMQ

 1.P1816 忠诚

题目描述

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

输入输出格式

输入格式:

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

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

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

输出格式:

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

输入输出样例

输入样例#1:
10 31 2 3 4 5 6 7 8 9 102 73 91 10
输出样例#1:
2 3 1
(*^__^*) 嘻嘻…… 代码:

1.AC代码——线段树做法
#include<iostream>#include<cstdio>#include<cmath>using namespace std;const int N = 2000003;int m,n,a,b,ans;struct Tree{    int l;int r;int w; }t[4*N];void build(int k,int ll,int rr) {    t[k].l=ll,t[k].r=rr;    if(ll == rr) {        scanf("%d",&t[k].w);        return ;    }    int mid=(ll+rr)/2;    build(k*2,ll,mid);    build(k*2+1,mid+1,rr);    t[k].w=min(t[k*2].w,t[k*2+1].w);}void ask(int k) {    if(t[k].l>=a&&t[k].r<=b) {        ans=min(ans,t[k].w);        return ;    }    int mid=(t[k].l+t[k].r)/2;    if(a<=mid) ask(k*2);    if(b>mid) ask(k*2+1);}int main() {    scanf("%d%d",&n,&m);    build(1,1,n);    for(int i=1; i<=n; i++) {        a=i-m,b=i-1;        if(i==1) {            cout<<0<<endl;            continue ;        }        if(a<1) a=1;        ans=0x7fffffff;        ask(1);        printf("%d\n",ans);    }    return 0;}
2.RMQ做法(会T掉)
#include<iostream>#include<cstdio>using namespace std;const int N = 10003;int n,m,x,y,s[N],q,log[N];int f[N][15],ans[N];int main() {    cin>>n>>m;    for(int i=1; i<=n; i++) cin>>s[i];    //log2 a = x,表示2的x次方=a     for(int i=2; i<=n; i++) log[i]=log[i>>1]+1; //log数组的下标表示 a,log数组中存的是2的多少次方     for(int i=1; i<=n; i++)         for(int j=1; j<=n; j++)             f[i][j]=0x7fffffff;    for(int i=1; i<=n; i++) f[i][0]=s[i];    for(int i=1,k=1; i<=log[n]; i++,k*=2) //k为2的i-1次方         for(int j=1; j+k-1<=n; j++) //j+k-1为左半端的最后一个             f[j][i]=min(f[j][i-1],f[j+k][i-1]);//i-1次方是一半     for(int i=1; i<=m; i++) {        cin>>x>>y;        int len=log[y-x+1];         ans[i]=min(f[x][len],f[y-(1<<len)+1][len]); //1<<len 表示2的len次方     }    for(int i=1; i<=m; i++)        cout<<ans[i]<<" ";    return 0;}

2.P1440 求m区间内的最小值

题目描述

一个含有n项的数列(n<=2000000),求出每一项前的m个数到它这个区间内的最小值。若前面的数不足m项则从第1个数开始,若前面没有数则输出0。

输入输出格式

输入格式:

第一行两个数n,m。

第二行,n个正整数,为所给定的数列。

输出格式:

n行,第i行的一个数ai,为所求序列中第i个数前m个数的最小值。

输入输出样例

输入样例#1:
6 27 8 1 4 3 2
输出样例#1:
077113

说明

【数据规模】

m≤n≤2000000

(*^__^*) 嘻嘻…… 代码:

1.AC代码——单调队列

#include<iostream>#include<cstdio>#include<queue>using namespace std;int q[2000003];int n,m,x,ans,a[2000003];int head=1,tail=1;int main() {     scanf("%d%d",&n,&m);    for(int i=1; i<=n; i++) scanf("%d",&a[i]);    printf("0\n");    q[1]=1;    for(int i=2; i<=n; i++) {        printf("%d\n",a[q[head]]);        if(q[head] <= i-m)             head++;        while(a[i] <= a[q[tail]] && head <= tail)            tail--;        q[++tail] = i;    }    return 0;}

2.线段树做法—(会T两个点)

#include<iostream>#include<cstdio>#include<cmath>using namespace std;const int N = 2000003;int m,n,a,b,ans;struct Tree{    int l;int r;int w; }t[4*N];void build(int k,int ll,int rr) {    t[k].l=ll,t[k].r=rr;    if(ll == rr) {        scanf("%d",&t[k].w);        return ;    }    int mid=(ll+rr)/2;    build(k*2,ll,mid);    build(k*2+1,mid+1,rr);    t[k].w=min(t[k*2].w,t[k*2+1].w);}void ask(int k) {    if(t[k].l>=a&&t[k].r<=b) {        ans=min(ans,t[k].w);        return ;    }    int mid=(t[k].l+t[k].r)/2;    if(a<=mid) ask(k*2);    if(b>mid) ask(k*2+1);}int main() {    scanf("%d%d",&n,&m);    build(1,1,n);    for(int i=1; i<=n; i++) {        a=i-m,b=i-1;        if(i==1) {            cout<<0<<endl;            continue ;        }        if(a<1) a=1;        ans=0x7fffffff;        ask(1);        printf("%d\n",ans);    }    return 0;}

 

自己选的路,跪着也要走完!!!

洛谷——RMQ