輸出這個區間上所有數的GCD,以及GCD與其相同的區間個數(整個序列)
一個連續區間的GCD,用倍增法預處理一下,就能做到 O(1)查詢
對於相同區間計數,就把詢問先離線一下
枚舉區間左端點,區間GCD是隨右端點遞減的,並且是階梯式的
並且由於GCD遞減的很快,這樣一個階梯只有幾層,可以當作log的
所以對於每一個GCD,二分右端點,就能求出答案
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <map>
using namespace std;
#define sint long long
#define maxn 110000
int gcd(int a,int b)
{
if(a>b) swap(a,b);
if(a==0) return b;
return gcd(b%a,a);
}
map<int,sint>mp;
int n,m,gd[maxn][20];
int query(int l,int r)
{
int k=(int)log2((double)(r-l+1));
return gcd(gd[l][k],gd[r-(1<<k)+1][k]);
}
void update(int pos)
{
int nowgcd=gd[pos][0];
int togcd=query(pos,n);
int margin=pos,ll,rr,mid,ans;
while(nowgcd!=togcd)
{
ll=margin+1;
rr=n;
ans=-1;
while(ll<=rr)
{
mid=(ll+rr)>>1;
if(query(pos,mid)!=nowgcd)
{
ans=mid;
rr=mid-1;
}
else ll=mid+1;
}
mp[nowgcd]+=ans-margin;
margin=ans;
nowgcd=query(pos,margin);
}
if(margin<=n)
{
mp[togcd]+=(n-margin)+1;
}
}
void solve()
{
mp.clear();
for(int i=1;i<=18;i++)
{
for(int j=1;j<=n&&j+(1<<i)-1<maxn;j++)
{
gd[j][i]=gcd(gd[j][i-1],gd[j+(1<<(i-1))][i-1]);
}
}
for(int i=1;i<=n;i++)
{
update(i);
}
scanf("%d",&m);
int l,r;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&l,&r);
int g=query(l,r);
printf("%d %lld\n",g,mp[g]);
}
}
int main()
{
int cas;
scanf("%d",&cas);
for(int i=1;i<=cas;i++)
{
printf("Case #%d:\n",i);
memset(gd,0,sizeof(gd));
scanf("%d",&n);
for(int j=1;j<=n;j++) scanf("%d",&gd[j][0]);
solve();
}
return 0;
}
/*
1
5
1 2 4 6 7
4
1 5
2 4
3 4
4 4
Case #1:
1 8
2 4
2 4
6 1
*/