给定一个序列,每次询问一个区间
输出这个区间上所有数的GCD,以及GCD与其相同的区间个数(整个序列)
一个连续区间的GCD,用倍增法预处理一下,就能做到 O(1)查询
对于相同区间计数,就把询问先离线一下
枚举区间左端点,区间GCD是随右端点递减的,并且是阶梯式的
并且由于GCD递减的很快,这样一个阶梯只有几层,可以当作log的
所以对于每一个GCD,二分右端点,就能求出答案
输出这个区间上所有数的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
*/