hdu 5726 GCD rmq 二分

rmq 预处理很裸
然后当l-r区间的最大公约数必然是第一个数a[l]的几个质因子相乘的形式,而它最多有log(1000000000)个质因子,所以枚举每个左端点二分次数时不会超过log(1000000000)次,但实际上脑补一下远没有这么多次不会超时,然后枚举每个左端点进行log(1000000000)次二分把结果存入map中

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<cmath>
#define maxn 100005
#define maxv 1000000000ll
using namespace std;
int n,dp[maxn][20],a[maxn],m;
map<int,long long>mp;
int gcd(int a,int b)
{
    while(b)
    {
       int t;
       t = a%b;
       a = b;
       b = t;
    }
    return a;
}
void rmq()
{
     for(int i=1;i<=n;i++)dp[i][0] = a[i];
     for(int i=1;i<=log(n)/log(2);i++)
         for(int j=1;j+(1<<i)-1<=n;j++)
             dp[j][i] = gcd(dp[j][i-1],dp[j+(1<<(i-1))][i-1]);
}
int query(int l,int r)
{
    int pre = log(r-l+1)/log(2);
    return gcd(dp[l][pre],dp[r-(1<<pre)+1][pre]);
}
void solve()
{
     for(int i=1;i<=n;i++)
     {
         int prePo = i;
         int preGCD = a[i];
         while(prePo<=n)
         {
              int l = prePo,r = n,ans = prePo;
              while(l<=r)
              {
                  int mid = (l+r)/2;
                  int st = query(i,mid);
                  if(st<preGCD)r = mid-1;
                  else {
                      if(st==preGCD)ans = mid;
                      l = mid + 1;
                  }
              }
              mp[preGCD] += (ans-prePo+1);
              prePo = ans+1;
              preGCD = gcd(preGCD,a[prePo]);
         }
     }
}
int main()
{
    int t;
    scanf("%d",&t);
    int i1 = 1;
    while(t--)
    {
        mp.clear();
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        scanf("%d",&m);
        rmq();
        solve();
        printf("Case #%d:\n",i1);
        i1++;
        while(m--)
        {
            int l,r;
            scanf("%d %d",&l,&r);
            int pre = query(l,r);
            printf("%d %I64d\n",pre,mp[pre]);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值