HDU 5726 GCD [RMQ+二分]

題幹

鏈接:走你

題解

/**
*@author micsay
*先用RMQ預處理各個區間的gcd,然後再用二分法+map統計相同gcd區間的區間個數
*具體解題思路請看註釋
**/
#include<cstdio>
#include<cmath>
#include<cstring>
#include<map>
//構建mp數組的函數
int _func(int a,int b){
  if(!a)return b;
  return _func(b%a,a);
}
typedef long long T;
typedef long long ll;
const int MAX = 100010;//原始數組大小
int root_num;//原始數組真正大小
T root[MAX];
T mp[MAX][65];//1<<65爲最大可以保存的64bit整數,mp保存RMQ預處理數據
void RMQ_init(){
  memset(mp,0,sizeof(mp));
  int lg2 = log(root_num)/log(2);
  for(int i=1;i<=root_num;i++)mp[i][0]=root[i];//將root數據導入mp(初始化)
  for(int j=1;j<=lg2;j++){
     for(int i=1;i<=root_num;i++){
        //構建mp[][]數組
        //分爲:區間一[i,i+(1<<(j-1))-1]和區間二[i+(1<<(j-1)),i+(1<<j)-1]兩部分
        //其中,區間[i,i+(1<<j)-1]的大小爲1<<j,所以正好每個區間大小爲1<<(j-1)
        mp[i][j]=mp[i][j-1];
        if(i+(1<<(j-1))<=root_num)
            mp[i][j]=_func(mp[i][j-1],mp[i+(1<<(j-1))][j-1]);
     }
  }
}
T query(int l,int r){
   int lg2 = log(r-l+1)/log(2);
   //區間[l,r]大小爲(r-l+1),可以分成兩個區間,分別爲:區間一[l,l+(1<<lg2)-1],
   //區間二[r-(1<<lg2)+1,log2](注意,兩個區間會有交集,但是並不影響結果)
   return _func(mp[l][lg2],mp[r-(1<<lg2)+1][lg2]);
}
//用map記錄
std::map<int,ll>V;
bool Same(const int l,int r,const T gcd){
   //看區間[l,r]的gcd是否與gcd相同
   return gcd == query(l,r);
}
//用二分法
//因爲區間[i,k](i<=k<=root_num)的gcd隨着k變大而呈遞減狀態
//所以比如區間[1,mid](mid=(1+root_num))的gcd和區間[1,root_num]的gcd相同,那麼,
//就不要再遍歷區間[1,p]了(mid<=p<root_num),而是直接使V[gcd]+=(root_num-mid+1)
//然後再遍歷[1,mid-1]
void find(const int l,int mid,int r,const T p_gcd){
   if(l>r)return;
   if(Same(l,mid,p_gcd)){
      if(V.find(p_gcd)==V.end()){
        V[p_gcd] = ll(r-mid+1);
      }else{
        V[p_gcd] += ll(r-mid+1);
      }
      r=mid-1;
      if(l<=r)
        find(l,(l+r)/2,r,query(l,r));
      return;
   }else{
      mid = mid + (r-mid)/2 + (r-mid)%2;
      find(l,mid,r,p_gcd);
   }
}
int main(){
  int t,q;
  scanf("%d",&t);
  for(int cas=1;cas<=t;cas++){
    scanf("%d",&root_num);
    for(int i=1;i<=root_num;i++){
      scanf("%I64d",&root[i]);
    }
    RMQ_init();
    V.clear();
    //構建V
    for(int i=1;i<=root_num;i++){
       find(i,(i+root_num)/2,root_num,query(i,root_num));
    }
    scanf("%d",&q);
    printf("Case #%d:\n",cas);
    int l,r;
    while(q--){
       scanf("%d%d",&l,&r);
       ll ans = query(l,r);
       printf("%I64d %I64d\n",ans,V[ans]);
    }
  }
  return 0;
}
發佈了40 篇原創文章 · 獲贊 0 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章