题意:最多给10W数据,然后最多有10W次询问,每次提问一个区间,问此区间的最大公约数是多少,同时和此区间的最大公约数相同的区间还有多少个?
10W次询问区间的gcd,并同时询问和这个gcd相等的区间还有多少个,第一问很容易解决,用rmq||线段树,第二问想了好久也没想出来不TLE的办法。后来问了学长,才知道应该用二分来解决。
对于多次询问,肯定要做预处理,尽量做到询问时O(1)的复杂度,rmq可以解决区间最值,即解决第一问。第二问可以先把所有的情况存一下,在询问的时候直接输出结果,因为数字很大,10E,所以用map来代替数组来存所有情况对应的个数。
对于如何二分来解决提前存放所有情况出现个数,常规操作是两层for循环遍历所有区间,但是时间复杂度为n^2,肯定TLE。但是若用二分+rmq查询,时间复杂度最快能达到nlogn。
基本思路是对应区间(l,n),不断通过二分+rmq查询来查找整段区间的最大公约数和起始值一样的区间,并不断更新起始值。
#include<stdio.h>
#include<math.h>
#include<map>
#include<algorithm>
using namespace std;
int gcd(int x,int y) //求最大公约数
{
while(x^=y^=x^=y%=x);
return y;
}
int a[100010][20],n;
void rmq() //rmq
{
int i,j;
for(i=1; i<19; i++)
for(j=1; j<=n; j++)
if(j+(1<<i)-1<=n)
a[j][i]=gcd(a[j][i-1],a[j+(1<<(i-1))][i-1]);
}
int query(int x,int y) //询问区间gcd
{
int s=(int)(log(y-x+1)/log(2));
return gcd(a[x][s],a[y-(1<<s)+1][s]);
}
int main()
{
int nn,ss=0;
scanf("%d",&nn);
while(nn--)
{
map<int,long long>m;
int i,j;
scanf("%d",&n);
for(i=1; i<=n; i++)
scanf("%d",&a[i][0]);
rmq();
for(i=1; i<=n; i++) //提前预处理,二分+区间询问
{
int temp=a[i][0];
for(j=i; j<=n; j++)
{
temp=gcd(temp,a[j][0]);
if(temp==1)
{
m[1]+=n-j+1;
break;
}
int l=j,r=n;
while(l<=r) //二分
{
int mid=(l+r)>>1;
int x=query(i,mid);
if(x==temp)
l=mid+1;
else
r=mid-1;
}
m[temp]+=r-j+1;
j=r;
}
}
int mm;
scanf("%d",&mm);
printf("Case #%d:\n",++ss);
while(mm--)
{
int x,y,gcd_xy;
scanf("%d%d",&x,&y);
gcd_xy=query(x,y);
printf("%d %lld\n",gcd_xy,m[gcd_xy]);
}
}
return 0;
}