HDU 5875 Function (ST表+二分 or 线段树)

24 篇文章 0 订阅
11 篇文章 1 订阅


Problem Description
 
The shorter, the simpler. With this problem, you should be convinced of this truth.
  
  You are given an array  A of N postive integers, and M queries in the form (l,r). A function F(l,r) (1lrN) is defined as:
F(l,r)={AlF(l,r1) modArl=r;l<r.
You job is to calculate F(l,r), for each query (l,r).
 

 

Input
 
There are multiple test cases.
  
  The first line of input contains a integer  T, indicating number of test cases, and T test cases follow. 
  
  For each test case, the first line contains an integer N(1N100000).
  The second line contains N space-separated positive integers: A1,,AN (0Ai109).
  The third line contains an integer M denoting the number of queries. 
  The following M lines each contain two integers l,r (1lrN), representing a query.
 

 

Output
 
For each query (l,r), output F(l,r) on one line.
 

 

Sample Input
 
1 3 2 3 3 1 1 3
 

Sample Output

 

2
 

题意:

  给你一个n,n个数

  m个询问,每次询问你 l,r,, a[l] % a[l+1] % a[l+2] %……a[r] 结果是多少

思路:

每次取模比自己小的数, 最多logn次就到0了, 因为 你每次取模自己小的数, 取模之后 最大的数就是 n/2, 每次都是n/2, logn次就是0了, 关键是怎么找每个数取模之后后面的第1个比他小的数,两种做法, 一个是 rmq+二分, 另一个是线段树,线段树可以解决好多问题的。。。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;
const int INF = 1e9 + 7;
const int maxn = 1e5 + 10;
int minx[maxn][50], a[maxn], n, lg2[maxn];
void RMQ()
{
    for(int i = 2;i <= n; i++)lg2[i] = lg2[i/2]+1;  //还快一点
    for(int i = 1;i <= n; i++) minx[i][0] = a[i];
    for(int j = 1;(1<<j) <= n; j++)
    {
        for(int i = 1; i+(1<<j)-1 <= n; i++){
            minx[i][j] = min(minx[i][j-1],minx[i+(1<<(j-1))][j-1]);
        }
    }
}
int check(int l, int r)
{
    int k = lg2[r-l+1];
    return min(minx[l][k], minx[r-(1<<k)+1][k]);
}
int main()
{
    int T, q, l, r;
//    RMQ();
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &n);
        for(int i = 1; i <= n; i++)
            scanf("%d", &a[i]);
        RMQ();
        scanf("%d", &q);
        while(q--)
        {
            scanf("%d%d", &l, &r);
            if(l == r)
            {
                printf("%d\n", a[l]);
                continue;
            }
            int k = a[l];
            l++;
            int la, ra, mid, tmp, flag;
            while(l <= r && k)
            {
                la = l, ra = r, flag = 0;
                while(la <= ra)  //这样快点
                {
                    mid = (la+ra) >> 1;
                    if(check(la, mid) <= k)
                        ra = mid - 1, tmp = mid, flag = 1;
                    else if(check(mid, ra) <= k)
                        la = mid + 1, tmp = mid, flag = 1;
                    else
                        break;
                }
                if(!flag) break;
                l = tmp+1;
                k %= a[tmp];
            }
            printf("%d\n", k);
        }
    }
    return 0;
}

线段树代码:

我们可以使用线段树求出区间内小于某个值的最前一个位置,具体方法就是:父节点记录区间最小值,接着当这一整段的最小值小于等于给定的值时就递归进此子树(另一棵子树还是可能递归,因为可能前一个子树包含的区间大于所求的区间),这样我们知道第一次递归到叶子节点时就一定是最前一个小于等于此值的位置(如果有这个值的话)。

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<string>
#include<cstdio>
#include<cstring>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define eps 1E-8
/*注意可能会有输出-0.000*/
#define Sgn(x) (x<-eps? -1 :x<eps? 0:1)//x为两个浮点数差的比较,注意返回整型
#define Cvs(x) (x > 0.0 ? x+eps : x-eps)//浮点数转化
#define zero(x) (((x)>0?(x):-(x))<eps)//判断是否等于0
#define mul(a,b) (a<<b)
#define dir(a,b) (a>>b)
typedef long long ll;
typedef unsigned long long ull;
const int Inf=1<<30;
const double Pi=acos(-1.0);
const int Mod=1e9+7;
const int Max=200010<<2;
int nnum[Max],cnt,flag;
int segtr[Max];
struct node
{
    int mmin,mpos;
}res;
void Upnow(int now,int next)
{
    segtr[now]=min(segtr[next],segtr[next|1]);
    return;
}
void Create(int sta,int enn,int now)
{
    if(sta==enn)
    {
        scanf("%d",&segtr[now]);
        nnum[cnt++]=segtr[now];//记录每个位置的值
        return;
    }
    int mid=dir(sta+enn,1);
    int next=mul(now,1);
    Create(sta,mid,next);
    Create(mid+1,enn,next|1);
    Upnow(now,next);
    return;
}
void Query(int sta,int enn,int now,int x,int y,int z)
{
    if(sta>=x&&enn<=y)
    {
        if(sta==enn)//到叶子节点
        {
            flag=1;//表示只能到一次叶子节点
            if(segtr[now]<=z)//找到
            {
                res.mmin=segtr[now];
                res.mpos=sta;
            }
            return;
        }
        if(segtr[now]>z)//这一段不需要再递归
        return;
    }
    int mid=dir(sta+enn,1);
    int next=mul(now,1);
    if(mid>=x&&!flag&&segtr[next]<=z)//之前没到叶子,子树区间最小值小于等于给定的值
         Query(sta,mid,next,x,y,z);
    if(mid<y&&!flag&&segtr[next|1]<=z)
        Query(mid+1,enn,next|1,x,y,z);
    return;
}
int main()
{
    int t,n,m;
    int lef,rig;
    scanf("%d",&t);
    while(t--)
    {
        cnt=1;
        scanf("%d",&n);
        Create(1,n,1);
        scanf("%d",&m);
        for(int i=1;i<=m;++i)
        {
            scanf("%d %d",&lef,&rig);
            if(lef==rig)//只有一个值
            {
                 printf("%d\n",nnum[lef]);
                 continue;
            }
            int ans=nnum[lef];
            lef++;
            while(1)
            {
                res.mmin=Inf,res.mpos=-1;
                flag=0;
                Query(1,n,1,lef,rig,ans);
                if(ans>=res.mmin)//成功取模
                {
                    ans=ans%res.mmin;
                    lef=res.mpos+1;
                }
                else
                    break;
                if(lef>rig||ans==0)//结束条件
                    break;
            }
            printf("%d\n",ans);
        }
    }
    return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值