题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5726
|
GCDTime Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 1151 Accepted Submission(s): 354
Problem 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
Sample Output
Author
HIT
Source
|
思路:先预处理一下f数组
f[i][j]=以i为起点的2^j个数 f[1][0]=gcd(a[1]) f[2][1]=fcd(a[2],a[3])...
可以知道 f[i][j]=gcd(f[i][j-1],f[i+(1<<j-1)][j-1]);
那么我们就可以轻松O(1)找到任意区间的gcd
比如找区间L,R 则可以这样算 int k=log2(R-L+1); return gcd(f[L][k],f[R-(1<<k)+1][k]);
用mp[value]记录区间gcd的值为value的个数。
再计算区间gcd值的个数的时候
我们先枚举起点,二分终点(因为一个区间的gcd值是单调不递增的嘛),然后不断更新mp[value]
具体过程还是看代码吧:
#include<map> #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; typedef long long LL; const int maxn=100005; map<int,LL>mp; LL gcd(LL x,LL y){return x%y==0?y:gcd(y,x%y);} LL n,a[maxn],f[111111][55]; void presolve()//预处理f数组,f[i][j]表示gcd(a[i],a[i+1]...a[i+2^j-1])也就是a[i] 为起点2^j个数的gcd { for(int i=n;i>=1;i--) { for(int j=0;i+(1<<j)-1<=n;j++) { if(j==0)f[i][j]=a[i]; else f[i][j]=gcd(f[i][j-1],f[i+(1<<j-1)][j-1]); } } } LL getgcd(int L,int R)//O(1)查询区间gcd; { int k=log2(R-L+1); return gcd(f[L][k],f[R-(1<<k)+1][k]); } int main() { int T; scanf("%d",&T); for(int t=1;t<=T;t++) { scanf("%lld",&n); memset(f,0,sizeof(f)); for(int i=1;i<=n;i++)scanf("%lld",&a[i]);; int q,l,r; presolve(); for(int i=1;i<=n;i++)//枚举左端点 { int start=i; while(start<=n) { int head=start; int tail=n; int value=getgcd(i,start); int cnt=0; while(head<=tail) { int mid=(head+tail)>>1; int temp=getgcd(i,mid); if(temp==value) { head = mid+1; cnt = mid; } if(temp<value) tail = mid-1; else head = mid+1,cnt=mid; } mp[value]+=cnt-start+1;//每次算完更新数量 start=tail+1; } } printf("Case #%d:\n",t); scanf("%d",&q); while(q--) { scanf("%d%d",&l,&r); int ans=getgcd(l,r); cout<<ans<<" "<<mp[ans]<<endl; } mp.clear(); } }