问题 A: 模(mod)

				问题 A: 模(mod)
		时间限制: 1 Sec  内存限制: 128 MB

题目描述
王小花喜欢模法,所以她生成了一个序列,没事干的时候模着玩.
王小花的序列a是一个长度为n的正整数序列。王小花每次会指定一个非负整数x和一个位置区间[l,r],并希望求出的值x mod a[l] mod a[l+1] ⋯ mod a[r−1] mod a[r]。
因为王小花知道暴力模不可取,所以她想让你帮忙用计算机求出答案。
输入
第一行输入两个正整数n,m,其中m是询问次数。
第二行输入n个正整数,第i个数表示a[i]。
接下来m行,每行输入三个整数x,l,r。
输出
对于每个询问,输出一行一个整数ans表示答案。
样例输入 Copy
4 2
4 3 9 6
7 1 2
777 3 4
样例输出 Copy
0
3
提示
对于100%的数据,n,m≤105

思路:
根据题意知道暴力不可取,所以思考优化一下。
仔细看这个式子:x mod a[l] mod a[l+1] ⋯ mod a[r−1] mod a[r]
暴力是要取模很多次,但实际上并非如此,我们可以算出一个数经过一次取模,他的大小最少会衰减一半。如 :
y=x+mod;
得x<mod;
得y>2*x;
好了,所以我们发现一个数w最多衰减log2 (w) 次。然后我们就要想出怎么找到从当前指针k到r这一段区间里第一个比x小的数就行了,然后怎么实现呢,考虑 st表上二分。
对于st表来说,固定左端点后,查询f[L][x],这个值随x得增加是有单调性的,然后就可以二分了,二分不太好写,我写的比较复杂,还用了递归,就常数比较大,要卡一下常数。
详细细节 见代码:

#pragma GCC optimize(2)
#pragma GCC optimize("Ofast","unroll-loops","omit-frame-pointer","inline")
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 400004;
int f[maxn][20];
int a[maxn];
int n,q;
void st()
{
    for(int i = 1; i <= n; i ++) f[i][0] = a[i];
    int t = log(n) / log(2) + 1;
    for(int j = 1; j < 20; j ++){
        for(int i = 1; i <= n - (1 << j) + 1; i ++){
            f[i][j] = min(f[i][j-1],f[i + (1 << (j - 1))][j - 1]);
        }
    }
}
int query(int x, int y)
{
    int t = log(abs(y-x + 1))/ log(2);
    int a = f[x][t];
    int b = f[y - (1 << t) + 1][t];
    return min(a,b);
}
void solve(int x,int L,int R)
{
    int l,r,mid;
    l=L;    r=R;
    if(l>r){
        printf("%d\n",x);  return ;
    }
    int op=query(l,r);
    if(op>x)   //如果[l,r]中所有的数都比x大,直接输出x。
    {
        printf("%d\n",x);  return ;
    }
    while(l<=r){
        mid=(l+r)/2;
        op=query(L,mid);
        if(op>x)
            l=mid+1;
        else
            r=mid-1;
    }
    l-=10;  l=max(L,l);
    for(int i=l;i<=R;i++){
        if(a[mid]<=x){
            mid=i;  break;
        }
    }
    /// 在区间[l,r]中找到第一个 小于等于 x的数的下标 , 此处用st表上二分实现,
    x%=a[mid];  mid++;
    solve(x,mid,R);
}
int main()
{
    int m,x,l,r;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    st();
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&x,&l,&r);
        x=x%a[l];
        solve(x,l,r);
    }
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值