hdu5875(取模的性质,线段树,二分)

题目链接

解法:
首先经过一段时间的思考后我们可以发现一个数取模只会受到小于等于它的模数的影响,而且取模的次数大概是log级别的,因为取一次至少减半,所以我们可以有一个简单的想法,就是找到这个数右边第一个小于等于它的数进行取模。然后我们用较为暴力的方法,二分加线段树,过去了。(我写的T了,好像常数太大,队友的过了,学习了一发姿势,二分嵌在线段树里写)。还要注意是两个端点都相等的时候进行操作,不是包含,因为涉及到左右关系啥的。要被正好顶着左边或者右边覆盖。

代码:

//
//  main.cpp
//  1008

#include 
#include 
#include 
#include 

using namespace  std;

#define lson o * 2,l,m
#define rson o * 2 + 1,m + 1,r

const int maxn = 1e5 + 5;

int minv[maxn << 2];
int a[maxn];

void build(int o,int l,int r){
    if(l == r){
        minv[o] = a[l];
        return;
    }
    int m = (l + r) >> 1;
    build(lson);
    build(rson);
    minv[o] = min(minv[o * 2],minv[o * 2 + 1]);
}

int query(int o,int l,int r,int ql,int qr,int p){
    if(p == 0) return 0;
    if(l == ql && r == qr){
        if(minv[o] > p) return p;
        int now,now_l,now_r,now_m;
        now_l = l;
        now_r = r;
        now = o;
        while(now_l < now_r){
            now_m = (now_l + now_r) >> 1;
            if(minv[now * 2] <= p){
                now = now * 2;
                now_r = now_m;
            }else{
                now = now * 2 + 1;
                now_l = now_m + 1;
            }
        }
        p %= minv[now];
        if(p == 0) return 0;
        return query(o,l,r,now_l + 1,r,p);
    }
    int m = (l + r) >> 1;
    if (ql <= m && minv[o * 2] <= p) p = query(o * 2,l,m,ql,min(qr,m),p);
    if (p == 0) return 0;
    if (qr > m && minv[o * 2 + 1] <= p) p = query(o * 2 + 1,m + 1,r,max(ql,m + 1),qr,p);
    return p;
}

int n,m;

int main(int argc, const char * argv[]) {
    int T;
    cin >> T;
    while(T--){
        cin >> n;
        for(int i = 1;i <= n;i++){
            scanf("%d",a + i);
        }
        build(1,1,n);
        cin >> m;
        for(int i = 1;i <= m;i++){
            int l,r;
            scanf("%d%d",&l,&r);
            if(l == r) printf("%d\n",a[l]);
            else{
                printf("%d\n",query(1, 1, n, l + 1, r, a[l]));
            }
        }
    }
    return 0;
}

你可能感兴趣的:(杂题)