HDU 5875 RMQ-ST

点击打开链接

题意:给n个数,然后m个询问,询问是L R,问从L开始一直向后取余的结果

思路:比赛的时候就有个想法,就是因为每个数取余的话只有比它小的才有影响,那么我们就找到比当前位置小的那个数的位置就可以了,中间略过的元素是没有影响的,然后因为一个数取余后结果至多变成一半,所以10的9次方的数最大的复杂度就是log10的9次方,然后找位置的话需要用二分,所以总的复杂度就是m*logn*log1e9,我和队友怎么算复杂度都不应该超时啊,知道今天才看到一个大神的博客,下面就是两个代码的差别,一个T,一个A

这是我的TLE的代码:

#include <math.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=100010;
int A[maxn],n,m,dp2[maxn][17];
inline int getint(){
    int res=0;
    char c=getchar();
    bool mi=false;
    while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
    while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
    return mi ? -res : res;
}
void rmq_init(){
    int i,j;
    for(i=1;i<=n;i++) dp2[i][0]=A[i];
    for(j=1;(1<<j)<=n;j++){
        for(i=1;i+(1<<j)-1<=n;i++){
            if(dp2[i][j-1]<dp2[i+(1<<(j-1))][j-1])
            dp2[i][j]=dp2[i][j-1];
            else
            dp2[i][j]=dp2[i+(1<<(j-1))][j-1];
        }
    }
}
int rmq(int x,int y){
    int m=ceil(log((double)(y-x+1))/log(2.0));//我的这里的询问是用一个数学公式推出来的
    if(dp2[x][m]<dp2[y-(1<<m)+1][m]) return dp2[x][m];
    else return dp2[y-(1<<m)+1][m];
}
int slove(int le,int ri){
    int i,pos=le+1,now=A[le];
    while(1){
        if(rmq(pos,ri)>now) break;
        int lll=pos,rrr=ri,ppp;
        while(rrr>=lll){
            int mid=(lll+rrr)>>1;
            if(rmq(lll,mid)<=now) rrr=mid-1,ppp=mid;
            else lll=mid+1;
        }
        now=now%A[ppp];pos=ppp+1;
        if(pos>ri) break;
        if(now==0) break;
    }
    return now;
}
int main(){
    int i,T,l,r;
    T=getint();
    while(T--){
        n=getint();
        for(i=1;i<=n;i++) A[i]=getint();
        rmq_init();
        m=getint();
        for(i=0;i<m;i++){
            l=getint();r=getint();
            if(l==r) printf("%d\n",A[l]);
            else printf("%d\n",slove(l,r));
        }
    }
    return 0;
}
这是现在AC的:
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=100010;
int A[maxn],n,m,dp2[maxn][20],Log[maxn];
void rmq_init(){
    int i,j;
    for(i=1;i<=n;i++) dp2[i][0]=A[i];
    for(j=1;(1<<j)<=n;j++){
        for(i=1;i+(1<<j)-1<=n;i++){
            if(dp2[i][j-1]<dp2[i+(1<<(j-1))][j-1])
            dp2[i][j]=dp2[i][j-1];
            else
            dp2[i][j]=dp2[i+(1<<(j-1))][j-1];
        }
    }
}
int rmq(int x,int y){
    int m=Log[y-x+1];//现在的查询无疑做到了O(1),下面的数组计算就是那个数学公式,先预处理的
    if(dp2[x][m]<dp2[y-(1<<m)+1][m]) return dp2[x][m];
    else return dp2[y-(1<<m)+1][m];
}
int slove(int le,int ri){
    int i,pos=le+1,now=A[le];
    while(1){
        if(rmq(pos,ri)>now) break;
        int lll=pos,rrr=ri,ppp;
        while(rrr>=lll){
            int mid=(lll+rrr)>>1;
            if(rmq(lll,mid)<=now) rrr=mid-1,ppp=mid;
            else lll=mid+1;
        }
        now=now%A[ppp];pos=ppp+1;
        if(pos>ri) break;
        if(now==0) break;
    }
    return now;
}
int main(){
    int i,T,l,r;
    Log[0]=-1;for(int i=1;i<maxn;i++) Log[i]=Log[i>>1]+1;//这里就是预处理
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(i=1;i<=n;i++) scanf("%d",&A[i]);
        rmq_init();
        scanf("%d",&m);
        for(i=0;i<m;i++){
            scanf("%d%d",&l,&r);
            if(l==r) printf("%d\n",A[l]);
            else printf("%d\n",slove(l,r));
        }
    }
    return 0;
}
PS:以后还是要注意,时间可以节省的就节省,你永远不知道哪里就TLE了,但是讲道理的说,上面的代码应该给AC把

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值