标签:
题目大概说给一个包含n个数的序列,多次询问有多少个区间GCD值等于某个区间的gcd值。
任何一个区间不同的GCD个数是log级别的,因为随着右端点向右延伸GCD是单调不增的,而每次递减GCD至少除以2。
考虑固定左端点,最多就nlogn种GCD,可以直接把所有区间GCD值预处理出来,用map存储各种GCD值的个数,查询时直接输出。
具体是这样处理的:枚举左端点,进行若干次二分查找,看当前GCD值最多能延伸到哪儿,进而统计当前GCD值的数量。
而求区间GCD,用ST表,预处理一下,就能在O(1)时间复杂度求出任意区间的gcd了。
1 #include<cstdio> 2 #include<cmath> 3 #include<map> 4 #include<algorithm> 5 using namespace std; 6 7 int gcd(int a,int b){ 8 while(b){ 9 int t=b; 10 b=a%b; 11 a=t; 12 } 13 return a; 14 } 15 16 int n,st[17][111111]; 17 void init(){ 18 for(int i=1; i<17; ++i){ 19 for(int j=1; j<=n; ++j){ 20 if(j+(1<<i)-1>n) continue; 21 st[i][j]=gcd(st[i-1][j],st[i-1][j+(1<<i-1)]); 22 } 23 } 24 } 25 int logs[111111]; 26 int query(int a,int b){ 27 int k=logs[b-a+1]; 28 return gcd(st[k][a],st[k][b-(1<<k)+1]); 29 } 30 31 int main(){ 32 for(int i=1; i<=100000; ++i){ 33 logs[i]=log2(i)+1e-6; 34 } 35 int t; 36 scanf("%d",&t); 37 for(int cse=1; cse<=t; ++cse){ 38 scanf("%d",&n); 39 for(int i=1; i<=n; ++i){ 40 scanf("%d",&st[0][i]); 41 } 42 43 init(); 44 45 map<int,long long> rec; 46 for(int i=1; i<=n; ++i){ 47 int g=st[0][i],j=i; 48 while(j<=n){ 49 int l=j,r=n; 50 while(l<r){ 51 int mid=l+r+1>>1; 52 if(query(i,mid)==g) l=mid; 53 else r=mid-1; 54 } 55 rec[g]+=(l-j+1); 56 j=l+1; 57 g=query(i,j); 58 59 } 60 } 61 62 printf("Case #%d:\n",cse); 63 int q,a,b; 64 scanf("%d",&q); 65 while(q--){ 66 scanf("%d%d",&a,&b); 67 int g=query(a,b); 68 printf("%d %lld\n",g,rec[g]); 69 } 70 } 71 return 0; 72 }
标签:
原文地址:http://www.cnblogs.com/WABoss/p/5686994.html