HDU 5726 GCD 【GCD】【ST表+二分】【线段树+暴力枚举】

题意

给一串数列,求区间GCD和整个数列中与该区间GCD相等的区间数

分析

首先区间GCD易求,用能求RMQ的方法都可以,比如ST表、线段树。关键是如何求第二个问题,这里有两种做法:
方法一
利用GCD的性质,若固定区间左端点,增大右端点,区间GCD必然非递增。因此我们可以遍历区间左端点,用二分求出以该端点起始的区间的所有gcd的情况及其对应的区间个数,并用map记录。该过程复杂度可近似看做O(nlogn)。写线段树有可能会T,最好写ST表。
方法二
用map暴力枚举。从左向右遍历,用两个map,一个存答案也就是某种gcd的个数,另一个存以当前位置结尾的所有区间的gcd情况。每向右移动一次,就将这个新元素与前一个位置结尾的情况全部gcd一遍,更新存答案的map,同时也更新另一个map

AC代码(方法一)

//HDU 5726 GCD 
//AC 2017-1-17 19:21:30
//Sparse table, GCD, Binary Search
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+100;

int N;
long long a[maxn];

long long ST[maxn][30];

void ST_init()
{
    for(int i=1;i<=N;++i)
        ST[i][0]=a[i];
    for(int j=1;j<30;++j)
    {
        for(int i=1;i<=N;++i)
        {
            if(i+(1<<j)-1>N) break;
            ST[i][j]=__gcd(ST[i][j-1],ST[i+(1<<(j-1))][j-1]);
        }
    }
    return;
}

long long ST_query(int s,int e)
{
    int k=(int)((log(e-s+1.0)/log(2.0)));
    return __gcd(ST[s][k],ST[e-(1<<k)+1][k]);
}

map<long long,long long> ans;

void setTable()
{
  ans.clear();
  for(int i=1;i<=N;i++)
  {
    int g=ST[i][0],j=i;
    while(j<=N)
    {
      int l=j,r=N;
      while(l<r)
      {
        int mid=(l+r+1)>>1;
        if(ST_query(i,mid)==g) l=mid;
        else r=mid-1;
      }
      ans[g]+=l-j+1;
      j=l+1;
      g=ST_query(i,j);
    }
  }
}

int main()
{
    int T;scanf("%d",&T);
    for(int kase=1;kase<=T;++kase)
    {
        scanf("%d",&N);
        for(int i=0;i<N;++i)
            scanf("%lld",a+i+1);
        ST_init();
        setTable();
        int Q;
        scanf("%d",&Q);
        int l,r;
        printf("Case #%d:\n",kase);
        while(Q--)
        {
            scanf("%d %d",&l,&r);
            long long gcd=ST_query(l,r);
            printf("%lld %lld\n",gcd,ans[gcd]);
        }
    }
    return 0;
}

AC代码(方法二)

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+100;

int N;
long long a[maxn];
struct segTree
{
    long long gcd[maxn*4];
    void Push_Up(int x)
    {
        gcd[x]=__gcd(gcd[x<<1],gcd[x<<1|1]);
        return;
    }
    void build(int x,int l,int r)
    {
        if(l==r)
        {
            gcd[x]=a[l];
            return;
        }
        int mid=(l+r)>>1;
        build(x<<1,l,mid);
        build(x<<1|1,mid+1,r);
        Push_Up(x);
        return;
    }
    long long query(int x,int beg,int endd,int l,int r)
    {
        if(l==beg&&r==endd)
            return gcd[x];
        int mid=(l+r)>>1;
        if(beg>mid)
            return query(x<<1|1,beg,endd,mid+1,r);
        else if(endd<=mid)
            return query(x<<1,beg,endd,l,mid);
        else
            return __gcd(query(x<<1,beg,mid,l,mid),query(x<<1|1,mid+1,endd,mid+1,r));
    }
}Tree;

map<long long,long long> ans;
map<long long,long long> mp1;
map<long long,long long> mp2;

int main()
{
    int T;scanf("%d",&T);
    for(int kase=1;kase<=T;++kase)
    {
        scanf("%d",&N);
        for(int i=0;i<N;++i)
            scanf("%lld",a+i+1);
        Tree.build(1,1,N);
        ans.clear();mp1.clear();mp2.clear();
        mp1[a[1]]++;ans[a[1]]++;
        for(int i=2;i<=N;++i)
        {
            mp2[a[i]]++;
            ans[a[i]]++;
            for(map<long long,long long>::iterator ita=mp1.begin();ita!=mp1.end();++ita)
            {
                long long now=__gcd(a[i],ita->first);
                ans[now]+=ita->second;
                mp2[now]+=ita->second;
            }
            swap(mp1,mp2);
            mp2.clear();
        }
        int Q;
        scanf("%d",&Q);
        int l,r;
        printf("Case #%d:\n",kase);
        while(Q--)
        {
            scanf("%d %d",&l,&r);
            long long gcd=Tree.query(1,l,r,1,N);
            printf("%lld %lld\n",gcd,ans[gcd]);
        }
    }
    return 0;
}

转载于:https://www.cnblogs.com/DrCarlluo/p/6580576.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值