ST learn & segment tree(auxiliary)

OI-wiki_STtable(sparse-table)

This algorithm bases on Doubling method to solve the RMQ

Conclusion(the main thoughts or ideas):

1. the doubling idea can transform O(n) to O(logn) 
2. the frequently used compute resource can be saved to avoid replicate computing
3. repeatable contribution question can be solved by ST algorithm and pay attention to the union of the two parts is the objective section 

RMQ :
template_ST
(In this code, I use the class to recall the use of class, but it's not suitable to do it in the algorithm problems)

#include<iostream>
#include<cmath>
#define LOCAL
using namespace std;

// 能力有限,这里只是个求整数区间最大值的类
class ST{
    private:
        int* table;
        int* log2d;         //the floor of log2(n) 
        int** STf;
        int len;
    public:
        void InitST(int* nums,int len);
        int getmax(int l,int r);
        ~ST();
};
//预处理pretreatment
void ST::InitST(int* nums,int sz){
    len=sz;
    // cout<<__LINE__<<endl;
    table = new int[len+5];
    for (int i=0;i<len;++i) table[i]=nums[i];
    log2d = new int[len+5];
    log2d[1]=0;
    for (int i=2;i<=len+1;++i) log2d[i]=log2d[i/2]+1;
    STf   = new int*[len+5];
    for (int i=0;i<len+5;++i) {STf[i]=new int[log2d[len]+5]; STf[i][0]=table[i];}
    // state transition equation : f(i,j) = max (f(i,j-1),f(i+2^(j-1),j-1))  
    // cout<<__LINE__<<endl;
    for (int j=1;j<=log2d[len]+1;++j){
    // cout<<__LINE__<<endl;
        for (int i=0;i+(1<<j)-1<len;++i){   //注意这个截止条件明显节约了时间(剪枝优化)
    // cout<<__LINE__<<endl;
            STf[i][j]=max(STf[i][j-1],STf[i+(1<<(j-1))][j-1]);  // 往往错误就在一个十分微小的地方

    // cout<<__LINE__<<endl;
        }
    }
}

int ST::getmax(int l,int r){
    int s=log2d[r-l+1];
    return max(STf[l-1][s],STf[r-1-(1<<s)+1][s]);  // there is repitition between these two intervals
}
ST::~ST(){
    delete[] table;
    delete[] log2d;
    for (int i=0;i<len+5;++i) delete[] STf[i];
    delete[] STf;
}

inline int read(){
    int f=1,s=0,c=getchar();
    while(c<'0' || c>'9') { if (c=='-') f=-1;c=getchar();}
    while(c>='0' && c<='9') { s=s*10+c-'0';c=getchar();}
    return s*f;
}
int array[100010];
int main(){
    #ifdef LOCAL
    freopen("input.txt","r",stdin);
    #endif
    int N=read(),M=read();
    for (int i=0;i<N;++i) array[i]=read();
    ST st1;
    // cout<<__LINE__<<endl;
    st1.InitST(array,N);
    while(M--){
        int s=read(),d=read();
        // cout<<__LINE__<<endl;
        printf("%d\n",st1.getmax(s,d));
    }
    return 0;
}

降雨量

This problem is easy to understand and solved by ST algorithm, but the judgement of true/maybe/false is complex

For my code :

1. the data of oj is tolerant and i need to find the discrete solution !!!
2. how to use my own method to judge the correctness (the encoding format)
3. study the solution in luogu and learn multiple methods to solve it 

my silly code :

//https://www.luogu.com.cn/problem/P2471
#include<iostream>
#include<cmath>
#define LOCAL
using namespace std;
// const int MAXN=5e4+10;
const int MAXY=1e5+10;
int n,start;
int l;
int rf[MAXY]={0};
int read(){
    int s=0,f=1,c=getchar();
    while(c<'0' || c>'9') { if (c=='-') f=-1;c=getchar();}
    while(c>='0' && c<='9'){ s=s*10+c-'0';c=getchar();}
    return s*f;
}
// pretreat Sparse Table
int log2d[MAXY];
int stfx[MAXY][40]; //max
int stfn[MAXY][40]; //min
void pretreat(int len){
    log2d[1]=0;
    for (int i=2;i<=len;++i) log2d[i]=log2d[i/2]+1;
    for (int i=0;i<len;++i) stfn[i][0]=stfx[i][0]=rf[i];
    for (int j=1;j<=log2d[len]+1;++j){
        for (int i=0;i+(1<<j)-1<len;++i){
            stfx[i][j]=max(stfx[i][j-1],stfx[i+(1<<(j-1))][j-1]);
            stfn[i][j]=min(stfn[i][j-1],stfn[i+(1<<(j-1))][j-1]);
        }
    }
}
int getmax(int l,int r){
    int s=log2d[r-l+1];
    return max(stfx[l][s],stfx[r-(1<<s)+1][s]);
}
int getmin(int l,int r){
    int s=log2d[r-l+1];
    return min(stfn[l][s],stfn[r-(1<<s)+1][s]);
}
// search and judge
void check(int id2, int id1){
    // cout<<getmax(id1+1,id2-1)<<endl;
    // cout<<id1<<"    "<<id2<<endl;
    // if (id1<0 || id1>l || id2<0 || id2>l) {
    //     printf("maybe\n");
    //     return;
    // }
    // if (rf[id1]>=rf[id2] && getmax(id1+1,id2-1)<rf[id2]){
    //     if (getmin(id1+1,id2-1)) printf("true\n");
    //     else printf("maybe\n");
    // }
    // else printf("false\n");
    
    //首先,为了防止数组越界,先解决完索引不在数据范围内的情况
    if (id2<0) {
        printf("maybe\n");
        return ;
    }
    if (id2>l){
        if (id1<l && id1>=0 && rf[id1] && getmax(id1+1,l)>=rf[id1]){
            printf("false\n");
            return ;
        }
        printf("maybe\n");
        return ;
    }
    if (id1<0 ){
        if (rf[id2] && id2 && getmax(0,id2-1)>=rf[id2]) {
            printf("false\n");
            return ;
        }
        printf("maybe\n");
        return ;
    }
    if (id2-id1==1) {
        if (rf[id1] && rf[id2]){
            if (rf[id1]<rf[id2]) printf("false\n");
            else printf("true\n");
        }
        else printf("maybe\n");
        return ;
    }
    if (rf[id1]==0 && rf[id2]){
        if (getmax(id1+1,id2-1)>=rf[id2]){
            printf("false\n");
            return ;
        }
        printf("maybe\n");
        return ;
    }
    if (rf[id2]==0 && rf[id1]){
        if (getmax(id1+1,id2-1)>=rf[id1]){
            printf("false\n");
            return ;
        }
        printf("maybe\n");
        return ;
    }
    if (!(rf[id1] || rf[id2])) {
        printf("maybe\n");
        return ;
    }
    if (rf[id1]>=rf[id2] && getmax(id1+1,id2-1)<rf[id2]){
        if (getmin(id1+1,id2-1)){
            printf("true\n");
            return ;
        }
        printf("maybe\n");
        return ;
    }
    printf("false\n");
    return ;
}


int main(){
    #ifdef LOCAL
    freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);
    #endif
    n=read();
    start=read();rf[0]=read();
    // cout<<start<<endl;
    // cout<<rf[0]<<endl;
    for (int i=1;i<n;++i) {
        if (i==n-1) rf[l=(read()-start)]=read();
        else rf[read()-start]=read();
    }
    // cout<<rf[4]<<endl;
    pretreat(l+1);
    int m=read();
    // cout<<getmin(0,5)<<endl;
    // 注意下面的函数参数,函数参数是从右往左压栈的所以第二个参数读的是第一个数(所以我将上面函数的两个参数的名称做了替换【性质一样,顺序不一样,所以】)
    while(m--) check(read()-start,read()-start);
    return 0;
}

However, when I watch the solution in luogu :
segment tree solution

Although, this solution is about segment tree, but its measure to judge false/maybe/true is worthy to refer to !

判断时:

    先判false:

      1、当右端点年份确定,且中间年份最大降雨量大于等于右端点降雨量

      2、当左端点年份确定,且中间年份最大降雨量大于等于左端点降雨量

      3、当左右端点年份都确定,且左端点降雨量小于等于右端点降雨量

    再判maybe:

      1、(当左右年份有一个或两个不确定的时候,要么是maybe,要么是false,,而false前面已经判断了,所以这里:当左右端点之差不等于左右端点年份之差的时候,要么是左右年份又不确定的(maybe了),要么是确定(那也是maybe了)) 当左右端点之差不等于左右端点年份之差(等价于年份不连续,也就是我前面所说的更好的判断区间连续的方法)

      2、左端点年份不确定

      3、右端点年份不确定

      (因为已经切掉false的情况了,那么剩下的情况中可以直接照上面的判断!)

    最后判断true , 若上面情况都不满足,那么肯定是true

And this solution is discrete and judge by the length of interval and the DIF of years

#include<bits/stdc++.h>
#define il inline
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define For(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
#define Max(a,b) ((a)>(b)?(a):(b))
using namespace std;
const int N=200005;
int n,m,num[N],ls[N],rs[N];
int ye[N],co[N];
bool f[N];
struct node{
    int ans,f;
};

il int gi(){
    int a=0;char x=getchar();bool f=0;
    while((x<'0'||x>'9')&&x!='-')x=getchar();
    if(x=='-')x=getchar(),f=1;
    while(x>='0'&&x<='9')a=(a<<3)+(a<<1)+x-48,x=getchar();
    return f?-a:a;
}

il void pushup(int rt){
    if(rs[rt<<1]==ls[rt<<1|1]-1)f[rt]=f[rt<<1]&f[rt<<1|1];
    rs[rt]=rs[rt<<1|1];ls[rt]=ls[rt<<1];
    num[rt]=Max(num[rt<<1],num[rt<<1|1]);
}

il void build(int l,int r,int rt){
    if(l==r){rs[rt]=ls[rt]=ye[l],num[rt]=co[l],f[rt]=1;return;}
    int m=l+r>>1;
    build(lson),build(rson);
    pushup(rt);
}

il node query(int L,int R,int l,int r,int rt){
    if(L<=l&&R>=r){
        node tmp;
        tmp.ans=num[rt],tmp.f=f[rt];
        return tmp;
    }
    int m=l+r>>1;
    node tmp;
    tmp.ans=0,tmp.f=1;
    if(L<=m){
        node x=query(L,R,lson);
        tmp.ans=Max(tmp.ans,x.ans);
        tmp.f&=x.f;
    }
    if(R>m){
        node x=query(L,R,rson);
        tmp.ans=Max(tmp.ans,x.ans);
        tmp.f&=x.f;
    }
    return tmp;
}

int main(){
    n=gi();
    For(i,1,n)ye[i]=gi(),co[i]=gi();
    build(1,n,1);
    m=gi();
    int x,y;
    while(m--){
        x=gi(),y=gi();
        if(x>=y){printf("false\n");continue;}
        int st=lower_bound(ye+1,ye+n+1,x)-ye,ed=lower_bound(ye+1,ye+n+1,y)-ye;      //巧妙处理的在一直年份范围之外的情况,优秀 !!!
        bool fl,fr;int op=0;    // 十分完美的代替,简洁又不失正确性 !!!
        fl=ye[st]==x,fr=ye[ed]==y;
        if(!fl)st--;    //包括了两个年份之间的所有年份 
        if(st+1<=ed-1)op=query(st+1,ed-1,1,n,1).ans;
        if((op>=co[ed]&&fr)||(co[st]<co[ed]&&fl&&fr)||(op>=co[st]&&fl))printf("false\n");
        else if(ed-st!=ye[ed]-ye[st]||!fr||!fl)printf("maybe\n");
        else printf("true\n");
    }
    return 0;
}

Conclusion:

首先,对于判断条件,我虽然思路也比较清晰了,但还是不够好,不够简洁对于这种问题,
应该分别从多个角度看看哪种更简洁,而一般像这种,从三个方面去判断可能更简洁,
不过我的确从这方面去想了,不过还是不够深入,没有总结出来这种既简洁又清晰的思路,
而当想的深入明白后,发现离散的也是如此容易实现
上一篇:(小奇JAVA面试)每日10道Java面试题打卡—Java基础篇2


下一篇:Oracle词汇表(事务表(transaction table)”)