洛谷——RMQ
阿新 • • 發佈:2017-05-17
amp 輸出格式 clu 每天 n) space printf while turn
1.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
(*^__^*) 嘻嘻…… 代碼:
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 2 7 8 1 4 3 2輸出樣例#1:
0 7 7 1 1 3
說明
【數據規模】
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