Balanced Lineup
题意:N cows (1 ≤ N ≤ 50,000)Q (1 ≤ Q ≤ 200,000)queries;每次查询一个区间[l,r];问区间中最高和最矮的牛相差多少?
本题适合ST的入门题;
讲讲对ST的简单用法的理解:ST是一个二维DP倍增的思想,二维[i][j]表示区间长度为(1<<j)但是由于包括区间端点,所以表示为 [i,i+(2^j)-1];这时要得到一个长度为2^j的区间的最值,需要建立在子结构[i][j-1]与[i+(1<<j-1)][j-1]的基础之上;这也就是DP思想了;编写程序时,可知要对每一个长度枚举左端点,就构成了两重循环;外层为最大区间长度的数位bin;
ST算法的时间复杂度是O(nlogn);logn就是n的二进制;同时对里层n的枚举;
ps:或许看其他人的ST的init()里层的循环是逆着递减的,但是我写的是正常的从1开始递增;其他人的想法是当枚举的位置i加上长度超出序列长度时得到的值就是该点后面数值的全部信息;但是是否需要这样呢。因为我们的查询O(1)是从左右边界相向覆盖;
这就要看查询时每个端点延伸的长度是否会大于总查询长度的1/2;(显然如果小于1/2那么为什么要出边界呢)同时看看query()就会知道查询时左右端点的覆盖长度几乎等于查询区间的长度,所以这不是必要的~~
8736K 875MS(有点慢)
#include<iostream> #include<cstdio> #include<cstring> #include<string.h> #include<algorithm> #include<map> #include<queue> #include<vector> #include<cmath> #include<stdlib.h> #include<time.h> #include<stack> #include<set> using namespace std; #define rep0(i,l,r) for(int i = (l);i < (r);i++) #define rep1(i,l,r) for(int i = (l);i <= (r);i++) #define rep_0(i,r,l) for(int i = (r);i > (l);i--) #define rep_1(i,r,l) for(int i = (r);i >= (l);i--) #define MS0(a) memset(a,0,sizeof(a)) #define MS1(a) memset(a,-1,sizeof(a)) #define inf 0x3f3f3f3f typedef __int64 ll; template<typename T> void read1(T &m) { T x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} m = x*f; } template<typename T> void read2(T &a,T &b){read1(a);read1(b);} template<typename T> void read3(T &a,T &b,T &c){read1(a);read1(b);read1(c);} template<typename T> void out(T a) { if(a>9) out(a/10); putchar(a%10+'0'); } const int MAXN = 50050; int a[MAXN],mn[MAXN][20],mx[MAXN][20]; void init(int n) { int bin = int(log(1.*n)/log(2.0));//bin表示的数位<=精确值 rep1(i,1,n) mn[i][0] = mx[i][0] = a[i]; rep1(i,1,bin){ rep1(j,1,n){ // [j,j + (2^i)-1] if(j+(1<<i)-1> n) break; mx[j][i] = max(mx[j][i-1],mx[j+(1<<i-1)][i-1]); mn[j][i] = min(mn[j][i-1],mn[j+(1<<i-1)][i-1]); } } } int query(int l,int r) { int bin = int(log(1.*(r-l+1))/log(2.0)); int m = max(mx[l][bin],mx[r-(1<<bin)+1][bin]);//O(1)的查找 int n = min(mn[l][bin],mn[r-(1<<bin)+1][bin]); return m - n; } int main() { int N,Q; while(scanf("%d%d",&N,&Q) == 2){ rep1(i,1,N) read1(a[i]); init(N); int l,r; while(Q--){ read2(l,r); out(query(l,r)); puts(""); } } return 0; }
使用线段树也做了一下 1716K 719MS
线段树由于不是O(1)的查询,所以不能直接在每个区间中直接返回mx-mn,这是子区间的最优解,就完全不是整个区间的最优解了;所以要保存每个区间的MX和MN;
同时线段树不需要判断各种边界(对ST不熟..),觉得写起来更顺畅;
#include<iostream> #include<cstdio> #include<cstring> #include<string.h> #include<algorithm> #include<map> #include<queue> #include<vector> #include<cmath> #include<stdlib.h> #include<time.h> #include<stack> #include<set> using namespace std; #define rep0(i,l,r) for(int i = (l);i < (r);i++) #define rep1(i,l,r) for(int i = (l);i <= (r);i++) #define rep_0(i,r,l) for(int i = (r);i > (l);i--) #define rep_1(i,r,l) for(int i = (r);i >= (l);i--) #define MS0(a) memset(a,0,sizeof(a)) #define MS1(a) memset(a,-1,sizeof(a)) #define inf 0x3f3f3f3f #define lson l, m, rt << 1 #define rson m+1, r, rt << 1|1 typedef __int64 ll; template<typename T> void read1(T &m) { T x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} m = x*f; } template<typename T> void read2(T &a,T &b){read1(a);read1(b);} template<typename T> void read3(T &a,T &b,T &c){read1(a);read1(b);read1(c);} template<typename T> void out(T a) { if(a>9) out(a/10); putchar(a%10+'0'); } const int MAXN = 50050; int mx[MAXN<<2],mn[MAXN<<2]; void pushup(int rt) { mx[rt] = max(mx[rt<<1],mx[rt<<1|1]); mn[rt] = min(mn[rt<<1],mn[rt<<1|1]); } void build(int l,int r,int rt) { if(l == r){ read1(mx[rt]); mn[rt] = mx[rt]; return ; } int m = l+r>>1; build(lson); build(rson); pushup(rt); } int MX,MN; void query(int L,int R,int l,int r,int rt) { if(L <= l && r <= R){ MX = max(MX, mx[rt]); MN = min(MN, mn[rt]); return ; } int m = l+r>>1; if(L <= m) query(L,R,lson); if(m < R) query(L,R,rson); } int main() { int N,Q; while(scanf("%d%d",&N,&Q) == 2){ build(1,N,1); int l,r; while(Q--){ read2(l,r); MX = 0,MN = inf; query(l,r,1,N,1); out(MX - MN); puts(""); } } return 0; }