HDU5726 GCD(二分 + ST表)
题目
Source
http://acm.hdu.edu.cn/showproblem.php?pid=5726
Description
Give you a sequence of N(N≤100,000) integers : a1,...,an(0<ai≤1000,000,000). There are Q(Q≤100,000) queries. For each query l,r you have to calculate gcd(al,,al+1,...,ar) and count the number of pairs(l′,r′)(1≤l<r≤N)such that gcd(al′,al′+1,...,ar′) equal gcd(al,al+1,...,ar).
Input
The first line of input contains a number T, which stands for the number of test cases you need to solve.
The first line of each case contains a number N, denoting the number of integers.
The second line contains N integers, a1,...,an(0<ai≤1000,000,000).
The third line contains a number Q, denoting the number of queries.
For the next Q lines, i-th line contains two number , stand for the li,ri, stand for the i-th queries.
Output
For each case, you need to output “Case #:t” at the beginning.(with quotes, t means the number of the test case, begin from 1).
For each query, you need to output the two numbers in a line. The first number stands for gcd(al,al+1,...,ar) and the second number stands for the number of pairs(l′,r′) such that gcd(al′,al′+1,...,ar′) equal gcd(al,al+1,...,ar).
Sample Input
1
5
1 2 4 6 7
4
1 5
2 4
3 4
4 4
Sample Output
Case #1:
1 8
2 4
2 4
6 1
分析
题目大概说给一个包含n个数的序列,多次询问有多少个区间GCD值等于某个区间的gcd值。
任何一个区间不同的GCD个数是log级别的,因为随着右端点向右延伸GCD是单调不增的,而每次递减GCD至少除以2。
考虑固定左端点,最多就nlogn种GCD,可以直接把所有区间GCD值预处理出来,用map存储各种GCD值的个数,查询时直接输出。
具体是这样处理的:枚举左端点,进行若干次二分查找,看当前GCD值最多能延伸到哪儿,进而统计当前GCD值的数量。
而求区间GCD,用ST表,预处理一下,就能在O(1)时间复杂度求出任意区间的gcd了。
代码
#include<cstdio> #include<cmath> #include<map> #include<algorithm> using namespace std; int gcd(int a,int b){ while(b){ int t=b; b=a%b; a=t; } return a; } int n,st[17][111111]; void init(){ for(int i=1; i<17; ++i){ for(int j=1; j<=n; ++j){ if(j+(1<<i)-1>n) continue; st[i][j]=gcd(st[i-1][j],st[i-1][j+(1<<i-1)]); } } } int logs[111111]; int query(int a,int b){ int k=logs[b-a+1]; return gcd(st[k][a],st[k][b-(1<<k)+1]); } int main(){ for(int i=1; i<=100000; ++i){ logs[i]=log2(i)+1e-6; } int t; scanf("%d",&t); for(int cse=1; cse<=t; ++cse){ scanf("%d",&n); for(int i=1; i<=n; ++i){ scanf("%d",&st[0][i]); } init(); map<int,long long> rec; for(int i=1; i<=n; ++i){ int g=st[0][i],j=i; while(j<=n){ int l=j,r=n; while(l<r){ int mid=l+r+1>>1; if(query(i,mid)==g) l=mid; else r=mid-1; } rec[g]+=(l-j+1); j=l+1; g=query(i,j); } } printf("Case #%d:\n",cse); int q,a,b; scanf("%d",&q); while(q--){ scanf("%d%d",&a,&b); int g=query(a,b); printf("%d %lld\n",g,rec[g]); } } return 0; }