Gym - 101201J Shopping(RMQ+二分)

題目地址:http://codeforces.com/gym/101201/attachments

思路:

1.題目相當於對於一數,依次對[L,R]區間內的數取模,結果即爲答案。

2.若對於區間內一大於v的數,其取模結果仍爲本身,無作用。則問題變爲快速尋找區間內第一個不大於v的數(區間內數無序)。

3.考慮二分區間[L,R],mid=(L+R)/2。若區間[L,mid]內最小值不大於v則說明第一個不大於v的數在該區間內,否則第一個不大於v的數在[mid+1,R]內,不斷二分即可找到該數。

4.每次找到該數的位置後,下次區間查找範圍爲[pos,R],直到該區間內不存在小於v的數或v已經爲0。

4.每次取模v%x<=v/2,則複雜度爲log級別,查找同樣爲log級,時間可接受。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define debug
using namespace std;
typedef long long LL;
const int maxn=200000+50;
int n,q;
LL a[maxn];
int preLog2[maxn];
LL stTable[maxn][20];
void st_prepare(int n,LL* arr)
{
    preLog2[1]=0;
    for(int i=2; i<=n; i++)
    {
        preLog2[i]=preLog2[i-1];
        if((1<<preLog2[i]+1)==i)
        {
            ++preLog2[i];
        }
    }
    for(int i=n-1; i>=0; i--)
    {
        stTable[i][0]=arr[i];
        for(int j=1; (i+(1<<j)-1)<n; j++)
        {
            stTable[i][j]=min(stTable[i][j-1],stTable[i+(1<<j-1)][j-1]);
        }
    }
}
LL query_min(int l,int r)
{
    int len=r-l+1,k=preLog2[len];
    return min(stTable[l][k],stTable[r-(1<<k)+1][k]);
}
int getPos(int l,int r,LL v)
{
    int L=l,R=r,ans=n+1;
    while(L<=R)
    {
        int mid=(L+R)/2;
        if(query_min(L,mid)<=v)
        {
            ans=mid;
            R=mid-1;
        }
        else
        {
            L=mid+1;
        }
    }
    return ans;
}
int main()
{
#ifdef debu
    freopen("in.txt","r",stdin);
#endif // debug
    scanf("%d%d",&n,&q);
    for(int i=0; i<n; i++) scanf("%lld",&a[i]);
    st_prepare(n,a);
    for(int i=0; i<q; i++)
    {
        LL v;
        int l,r,pos;
        scanf("%lld%d%d",&v,&l,&r);
        l--,r--;
        while(l<=r)
        {
            v%=a[l];
            if(!v) break;
            l=getPos(l,r,v);
        }
        printf("%lld\n",v);
    }
    return 0;
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章