HDU 4630 No Pain No Game 解题报告

题目

2013 暑假多校训练 3

题意:

一个1到n的排列,每次查询在[l,r]这个区间中,任选两个数的最大公约数的最大值。

题解:

对1到n的每个i,找到i,2×i,3×i……的位置,排序后可以得到一串位置,只要查询的区间包含其中两个,答案就至少是i。也就是查询区间包含任意相邻的两个点形成的区间即可。

求出上述的所有位置区间,以右端点为第一关键字和查询区间一起排序(若右端点相同位置区间应放前面)。然后遍历这个数组,当遇到一个位置区间时,将i插入树状数组,下标为左端点,若是查询区间,则查询树状数组中左端点到右端点(直接查到MAXN也可以,因为在右端点之后的还没有插进去)的最大值,就是答案。

注意答案至少为1,且l=r时输出0.


//Time:1125ms
//Memory:8323KB
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <map>
#include <vector>
#define MAXN 50010
#define MAXM 600010
#define MP(x,y) make_pair((x),(y))
#define FI first
#define SE second
using namespace std;
int tree[MAXN],num[MAXN],to[MAXN],sta[MAXN],stop;
pair<pair<int,int>,int > pa[MAXM];
int ans[MAXN];
void add(int h,int num)
{
    h=MAXN-1-h;
    while(h<MAXN)
    {
        tree[h]=max(tree[h],num);
        h+=h&(-h);
    }
}
int query(int h)
{
    int ret=0;
    h=MAXN-1-h;
    while(h>0)
    {
        ret=max(ret,tree[h]);
        h-=h&(-h);
    }
    return ret;
}
int main()
{
    //freopen("/home/moor/Code/input","r",stdin);
    int ncase,n,q,top=0;
    scanf("%d",&ncase);
    while(ncase--)
    {
        top=0;
        memset(tree,0,sizeof(tree));
        scanf("%d",&n);
        for(int i=1;i<=n;++i)   scanf("%d",&num[i]),to[num[i]]=i;
        for(int i=2;i<=n;++i)
        {
            stop=0;
            for(int j=i;j<=n;j+=i)
                sta[stop++]=to[j];
            if(stop==1) continue;
            sort(sta,sta+stop);
            for(int j=0;j<stop-1;++j)   pa[top++]=MP(MP(sta[j+1],-i),sta[j]);
        }

        scanf("%d",&q);
        for(int i=1;i<=q;++i)
        {
            int l,r;
            scanf("%d%d",&l,&r);
            pa[top++]=MP(MP(r,i),l);
        }
        sort(pa,pa+top);
        for(int i=0;i<top;++i)
            if(pa[i].FI.SE<0)
                add(pa[i].SE,-pa[i].FI.SE);
            else    if(pa[i].SE==pa[i].FI.FI)
                ans[pa[i].FI.SE]=0;
            else    ans[pa[i].FI.SE]=max(1,query(pa[i].SE));
        for(int i=1;i<=q;++i)   printf("%d\n",ans[i]);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值