[区间GCD预处理 树状数组 离线] HDU 5869 Different GCD Subarray Query

首先确定一个右端点 向左做后缀gcd的值是不超过log的 因为gcd必然递减 每次至少除以2
那么可以对每个右端点预处理出来
然后要求区间不同的gcd个数 这里要用到1878: [SDOI2009]HH的项链的技巧
用树状数组离线处理
按右端点的顺序处理询问
令pos[x]等于x这个值出现的最靠右的位置 显然这里是对计算贡献最有利的 然后用树状数组维护下 对于某个位置 有多少个pos的取值是在这里的

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#define cl(x) memset(x,0,sizeof(x))
using namespace std;
typedef long long ll;

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
  return *p1++;
}

inline int read(int &x){
  char c=nc(),b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1; else if (c==EOF) return 0;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b; return 1;
}

const int N=100005;

int n,m,a[N];

struct abcd{
  int l,r,d;
  abcd(int l=0,int r=0,int d=0):l(l),r(r),d(d) {}
}G[N][35];
int cnt[N];

inline void Init(){
  for (int i=1;i<=n;i++){
    for (int j=1;j<=cnt[i-1];j++)
      G[i][j]=G[i-1][j],G[i][j].d=__gcd(G[i][j].d,a[i]);
    cnt[i]=cnt[i-1]+1;
    G[i][cnt[i]]=abcd(i,i,a[i]);
    int pnt=0;
    for (int j=1;j<=cnt[i];j++){
      if (G[i][j].d!=G[i][pnt].d)
    G[i][++pnt]=G[i][j];
      else
    G[i][pnt].r=G[i][j].r;
    }
    cnt[i]=pnt;
  }
}

namespace BIT{
  int maxn; ll c[N];
  inline void init(int n){ maxn=n; for (int i=1;i<=n;i++) c[i]=0; }
  inline void add(int x,int r){ for (int i=x;i<=maxn;i+=i&-i) c[i]+=r; }
  inline ll sum(int x) { int ret=0; for (int i=x;i;i-=i&-i) ret+=c[i]; return ret; }
  inline ll sum(int l,int r) { return sum(r)-sum(l-1); }
}

struct event{
  int l,r,idx;
  bool operator < (const event &B) const{ return r<B.r; }
}eve[N];
int tot;
ll ans[N];

int pos[1000005];

int main(){
  int T,l,r,Case=0;
  freopen("t.in","r",stdin);
  freopen("t.out","w",stdout);
  while (read(n)){
    read(tot); 
    for (int i=1;i<=n;i++) read(a[i]);
    Init(); BIT::init(n);
    for (int i=1;i<=tot;i++) read(eve[i].l),read(eve[i].r),eve[i].idx=i;
    sort(eve+1,eve+tot+1);
    int pnt=1;
    for (int i=1;i<=n;i++){
      for (int j=1;j<=cnt[i];j++){
    int d=G[i][j].d,r=G[i][j].r;
    if (pos[d]) BIT::add(pos[d],-1);
    pos[d]=max(r,pos[d]); BIT::add(pos[d],1);
      }
      while (pnt<=tot && eve[pnt].r==i)
    ans[eve[pnt].idx]=BIT::sum(eve[pnt].l,eve[pnt].r),pnt++;
    }
    for (int i=1;i<=tot;i++)
      printf("%I64d\n",ans[i]);
    cl(pos);
  }
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值