GCD(ST+二分)

GCD

 

 

 

 

题意:给出一个数列 ,m次询问,每次询问 l,r区间内的gcd值 和 与该区间gcd值相同的区间有多少个

思路: 枚举每一个左端点,找每个左端点对应的所有gcd值区间,预处理出来,由于gcd值呈阶梯下降,所以完全可以处理,此时顺便用map统计区间个数 一开始考虑的是用线段树取gcd值,在加上二分处理,但是发现这样做其实复杂度达到了 t*n*logn*logn*logn ,直接T掉,然后想到用RMQ O(1)去处理gcd值,降下一个logn成功AC

 

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <map>
 5 #include <cmath>
 6 #include <algorithm>
 7 #include <functional>
 8 using namespace std;
 9 typedef long long ll;
10 const int maxn=100010;
11 const int inf=0x3f3f3f3f;
12 int n;
13 int a[maxn];
14 int st[maxn][30];
15 int Log2[maxn];
16 
17 int Gcd(int a,int b){
18     return b==0?a:Gcd(b,a%b);
19 }
20 
21 /***************ST打表*******************************/
22 void init(){                        //预处理
23     for(int i=0;i<=n;i++){
24         if(i==0){
25             Log2[i]=-1;
26         }
27         else{
28             Log2[i]=Log2[i>>1]+1;
29         }
30     }
31     for(int j=1;(1<<j)<=n;j++){
32         for(int i=1;i+(1<<j)<=n+1;i++){
33             st[i][j]=Gcd(st[i][j-1],st[i+(1<<(j-1))][j-1]);
34         }
35     }
36 }
37 
38 int query(int l,int r){             //询问l~r之间数的gcd
39     int k=Log2[r-l+1];
40     return Gcd(st[l][k],st[r-(1<<k)+1][k]);
41 }
42 
43 /****************二分*********************************/
44 map<int,ll>cnt;
45 void Binary(int i){                 //二分找从i开始往后的所有gcd,因为数(不同的数)越多,gcd越小,gcd单调不递增,就可以用二分了
46     int l,r,ri,le,v;
47     l=i;                            //一开始走右边界都是i
48     r=i;
49     while( r<=n ){
50         le=r;                       //右边界的最左
51         ri=n;                       //右边界的最右,为二分右边界的位置做准备
52         v=query(l,r);               //求出一开始的l~r的gcd
53         while(le<=ri){              //二分找gcd==v的最右端,这样最右端到l之间的数只能使val保持与l~r之间的gcd相同
54             int mid=(le+ri)>>1;
55             if(query(l,mid)==v){
56                 le=mid+1;
57             }
58             else ri=mid-1;
59         }
60         cnt[v]+=le-r;
61         r=le;
62     }
63 }
64 
65 int main()
66 {
67     int t,q,v;
68     int cas=1;
69     scanf("%d",&t);
70     while( t-- ){
71         scanf("%d",&n);
72         for(int i=1;i<=n;i++){
73             scanf("%d",&a[i]);
74             st[i][0]=a[i];
75         }
76         init();
77         cnt.clear();
78         for(int i=1;i<=n;i++){
79             Binary(i);
80         }
81         scanf("%d",&q);
82         printf("Case #%d:\n",cas++);
83         for(int j=0;j<q;j++){
84             int x,y;
85             scanf("%d%d",&x,&y);
86             v=query(x,y);
87             printf("%d %lld\n",v,cnt[v]);
88         }
89     }
90     return 0;
91 }

 

posted @ 2020-02-07 16:47  swsyya  阅读(228)  评论(0编辑  收藏  举报

回到顶部