倍增,给出一个数,让它模一连串的数

                   J   Shopping

链接:http://codeforces.com/gym/101201 

题意:

给出一系列商品的价格,下面再给出q个人浏览商品的起点到末尾,和他带上的钱,如果看到能买的商品就买最多个,输出每个人浏览后所剩余的钱

分析:

如果对于每个人都遍历一遍的话,最坏的情况超过了1e9。

由于是取模运算,可以找到这个数列的第一个比他小的数,模它,比它大则无视。一个数最多模lgn下。

接下来最重要的是找到第一个比V小的数的位置

用倍增的方法存储一段数的最小值。

然后再以lgn的速度求出从a开始,比v小的第一个数。

ac代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn=200000+10;
#define LL long long
LL num[maxn];
LL Min[maxn][25];//Min[i][j]从第i个元素到i+2ej-1个元素的最小值
int fin(LL V,int s)
{
    for(int i=20;i>=1;i--)
    {
        while(Min[s][i]<=V&&Min[s][i-1]>V)
        {
            s+=(1<<(i-1));
        }
    }
    if(num[s]<=V)
       return s;
    else
       return maxn;
}
int main()
{
    int n,q,s,o;
    LL V;
    cin>>n>>q;
    for(int i=1;i<=n;i++)
    {
        scanf("%I64d",&num[i]);
        Min[i][0]=num[i];
    }
    for(int j=1;j<=20;j++)//建立倍增数组
    for(int i=1;i<=n;i++)
    {
        if(i+(1<<(j-1))<=n)
            Min[i][j]=min(Min[i][j-1],Min[i+(1<<(j-1))][j-1]);
        else
            Min[i][j]=Min[i][j-1];
    }
    for(int i=1;i<=q;i++)
    {
        scanf("%I64d %d %d",&V,&s,&o);
        int k=fin(V,s);
        while(k<=o)
        {
            V%=num[k];
            k=fin(V,k+1);
        }
        printf("%I64d\n",V);
    }
    return 0;
}
View Code

猜你喜欢

转载自www.cnblogs.com/carcar/p/8918767.html