題幹
鏈接:走你
題解
/**
*@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;
}