poj 3264 Balanced Lineup

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;
}
View Code

 使用线段树也做了一下 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;
}
View Code