HDU 4630 No Pain No Game (线段树离线查询)

题目地址:HDU 4630
这题一看数据范围,于是一直在思考n*logn的算法。。实在没想到好方法,找了找题解,发现都是用的n*sqrt(n)*logn的方法。。。算了算,这个复杂度的确可以过。。好吧。。
然后就可以先离线下来将询问按r值排序,然后枚举每个数,并且用sqrt(n)的方法枚举所有的约数,然后对于每个约数,对最近的一次出现的这个约数的地方进行更新。因为对于当前区间来讲,只要最近的这个地方更新了,那么前面的查询肯定都能查到这个地方的最大值,所以前面的不用更新。
代码如下:

#include <iostream>
#include <string.h>
#include <math.h>
#include <queue>
#include <algorithm>
#include <stdlib.h>
#include <map>
#include <set>
#include <stdio.h>
#include <time.h>
using namespace std;
#define LL long long
#define pi acos(-1.0)
#pragma comment(linker, "/STACK:1024000000")
#define root 1, n, 1
#define lson l, mid, rt<<1
#define rson mid+1, r, rt<<1|1
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
const double eqs=1e-9;
const int MAXN=50000+10;
int a[MAXN], Max[MAXN<<2], pre[MAXN], ans[MAXN];
struct node
{
        int l, r, id;
}fei[MAXN];
bool cmp(node x, node y)
{
        return x.r<y.r;
}
void PushUp(int rt)
{
        Max[rt]=max(Max[rt<<1],Max[rt<<1|1]);
}
void Update(int p, int x, int l, int r, int rt)
{
        if(l==r){
                if(Max[rt]<x) Max[rt]=x;
                return ;
        }
        int mid=l+r>>1;
        if(p<=mid) Update(p,x,lson);
        else Update(p,x,rson);
        PushUp(rt);
}
int Query(int ll, int rr, int l, int r, int rt)
{
        if(ll<=l&&rr>=r){
                return Max[rt];
        }
        int mid=l+r>>1, ans=0;
        if(ll<=mid) ans=max(ans,Query(ll,rr,lson));
        if(rr>mid) ans=max(ans,Query(ll,rr,rson));
        return ans;
}
int main()
{
        int t, n, i, q, r, j, tmp;
        scanf("%d",&t);
        while(t--){
                scanf("%d",&n);
                memset(Max,0,sizeof(Max));
                memset(pre,0,sizeof(pre));
                for(i=1;i<=n;i++){
                        scanf("%d",&a[i]);
                }
                scanf("%d",&q);
                for(i=0;i<q;i++){
                        scanf("%d%d",&fei[i].l,&fei[i].r);
                        fei[i].id=i;
                }
                sort(fei,fei+q,cmp);
                r=0;
                for(i=1;i<=n;i++){
                        for(j=1;j*j<=a[i];j++){
                                if(a[i]%j) continue ;
                                tmp=j;
                                if(pre[tmp]){
                                        Update(pre[tmp],tmp,root);
                                }
                                pre[tmp]=i;
                                if(j*j==a[i]) continue ;
                                tmp=a[i]/j;
                                if(pre[tmp]){
                                        Update(pre[tmp],tmp,root);
                                }
                                pre[tmp]=i;
                        }
                        while(r<q&&fei[r].r<=i){
                                ans[fei[r].id]=Query(fei[r].l,fei[r].r,root);
                                r++;
                        }
                }
                for(i=0;i<q;i++){
                        printf("%d\n",ans[i]);
                }
        }
        return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值