【题目链接】 http://acm.hdu.edu.cn/showproblem.php?pid=5875

 

【题目大意】

  给出一个数列,同时给出多个询问,每个询问给出一个区间,要求算出区间从左边开始不断对下一个数取模之后的结果。

 

【题解】

  考虑取模的递减性质,最多取模logn次,因此如何快速找出下一个取模的位置是解决这道题的关键,首先利用ST表预处理区间最小值,之后我们每次二分取模起点到终点这个区段,每次优先查询左区间(类似于线段树上的路径查询),这样子就能logn找到取模的位置,然后直接取模就好。

 

【代码】

#include <cstdio>  
#include <cstring>  
#include <algorithm> 
using namespace std;  
const int N=200010;    
int d[N][30],f[N][30],a[N],lg2[N];   
int T,n,m,l,r,q;  
void rmq_init(int n){  
    for(int i=2;i<=n;i++)lg2[i]=lg2[i/2]+1;
    for(int i=1;i<=n;i++)scanf("%d",&f[i][0]);
    for(int j=1;(1<<j)<=n;j++){  
        for(int i=1;i+(1<<j)-1<=n;i++){  
            f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
        }  
    }  
} 
int rmq_min(int l,int r){int k=lg2[r-l+1];return min(f[l][k],f[r-(1<<k)+1][k]);}
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        rmq_init(n);
        scanf("%d",&m);
        while(m--){
            scanf("%d%d",&l,&r);
            if(l==r){
                  printf("%d\n",f[l][0]);
                  continue;
            }
            int flag=1,ans=f[l][0],L=l+1,R=r;
            while(flag){
                while(L<R){
                    int mid=(L+R)>>1;
                    if(rmq_min(L,mid)<=ans)R=mid;
                    else{
                        if(rmq_min(mid+1,R)<=ans)L=mid+1;
                        else{flag=0;break;}
                    }
                }if(flag)ans=ans%f[L][0];
                if(L==r)break; 
                        L++; R=r;
            }printf("%d\n",ans);
        }
    }return 0;
}

愿你出走半生,归来仍是少年