http://acm.hdu.edu.cn/showproblem.php?pid=5726
给n个数,m次询问,每个询问【l,r】
求【l,r】之间的gcd是多少,并求出整个数组中有多少段的gcd等于该【l,r】的gcd
首先用st表预处理RMQ的gcd,然后枚举起点【1-n】i
由于gcd一段固定以后具有单调性的
对于每个起点,枚举右端点pos,记下i到pos的gcd为X,则在【pos,n】里二分查找最远的gcd仍为X的下标next,则说明从i开始到pos,与i到next之间的gcd都是同样的,因此mp[x]+=(next-pos+1)
每次case,nlogn预处理st表,预处理所有gcd个数大约时间为nlognlogn,查询o(1)+o(logn)
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <iostream>
using namespace std;
const int maxn=112345;
int h[maxn];
int mx[maxn][17];
int n,q;
int min(int a,int b)
{
return a<b?a:b;
}
int max(int a,int b)
{
return a>b?a:b;
}
int gcd(int a,int b)
{
if(!b) return a;
return gcd(b,a%b);
}
void rmq_init()
{
int i,j;
for(j=1; j<=n; j++) mx[j][0]=h[j];
int m=floor(log((double)n)/log(2.0));
for(i=1; i<=m; i++)
{
for(j=n; j>0; j--)
{
mx[j][i]=mx[j][i-1];
if(j+(1<<(i-1))<=n) mx[j][i]=gcd(mx[j][i],mx[j+(1<<(i-1))][i-1]);
}
}
}
int rmq_gcd(int l,int r)
{
int m=floor(log((double)(r-l+1))/log(2.0));
int b=gcd(mx[l][m],mx[r-(1<<m)+1][m]);
return b;
}
int bin(int start,int x,int y,int pre_gcd)
{
int l=x,r=y,ans;
while(l<=r)
{
int mid=(l+r)/2;
if (rmq_gcd(start,mid)>=pre_gcd)
l=mid+1,ans=mid;
else r=mid-1;
}
return ans;
}
map<int ,long long> mp;
int main()
{
int tt;
cin>>tt;
int cnt=1;
while(tt--)
{
cin>>n;
mp.clear();
for (int i=1; i<=n; i++) scanf("%d",&
h[i]);
rmq_init(); //********
for (int i=1; i<=n; i++)
{
int pre_gcd,pos=i;
while(pos<=n)
{
pre_gcd=rmq_gcd(i,pos);
int next=bin(i,pos,n,pre_gcd);
mp[pre_gcd]+=(long long )(next-pos+1);
pos=next+1;
}
}
int m;
cin>>m;
int x,y;
printf("Case #%d:\n",cnt++);
for (int i=1; i<=m; i++)
{
scanf("%d %d",&x,&y);
int ans=rmq_gcd(x,y);
printf("%d %lld\n",ans,mp[ans]);
}
}
return 0;
}

本文介绍了一种解决区间GCD查询与统计问题的有效算法。通过预处理ST表实现快速查询任意区间的最大公约数(GCD),并统计整个数组中相同GCD的区间数量。算法的时间复杂度为预处理阶段的nlogn及查询阶段的O(1)+O(logn)。

被折叠的 条评论
为什么被折叠?



