Codeforces 359D Pair of Numbers | 二分+ST表+gcd
题面:
给一个序列,求最长的合法区间,合法被定义为这个序列的gcd=区间最小值
输出最长合法区间个数,r-l长度
接下来输出每个合法区间的左端点
题解:
由于区间gcd满足单调性,所以我们可以二分区间长度,用st表维护区间最小值和gcd即可
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<cmath> 5 #define N 300100 6 using namespace std; 7 int gcd(int x,int y) 8 { 9 return y==0?x:gcd(y,x%y); 10 } 11 int rgcd[N][21],rmin[N][21],n,l,r,mid,ans,ok[N]; 12 int check(int len) 13 { 14 if (len==0) return 1; 15 for (int i=1;i+len<=n;i++) 16 { 17 int tmp=log2(len+1); 18 if (gcd(rgcd[i][tmp],rgcd[i+len-(1<<tmp)+1][tmp]) == min(rmin[i][tmp],rmin[i+len-(1<<tmp)+1][tmp])) 19 return 1; 20 } 21 return 0; 22 } 23 int main() 24 { 25 scanf("%d",&n); 26 for (int i=1,x;i<=n;i++) 27 scanf("%d",&x),rgcd[i][0]=rmin[i][0]=x; 28 for (int j=1;j<=20;j++) 29 for (int i=1;i+(1<<j)-1<=n;i++) 30 rgcd[i][j]=gcd(rgcd[i][j-1],rgcd[i+(1<<j-1)][j-1]),rmin[i][j]=min(rmin[i][j-1],rmin[i+(1<<j-1)][j-1]); 31 r=2*n; 32 while (l<r) 33 { 34 mid=l+r+1>>1; 35 if (check(mid)) 36 l=mid; 37 else r=mid-1; 38 } 39 for (int i=1;i+l<=n;i++) 40 { 41 int tmp=log2(l+1); 42 if (gcd(rgcd[i][tmp],rgcd[i+l-(1<<tmp)+1][tmp]) == min(rmin[i][tmp],rmin[i+l-(1<<tmp)+1][tmp])) 43 ok[i]=1,ans++; 44 } 45 printf("%d %d\n",ans,l); 46 for (int i=1;i<=n;i++) 47 if (ok[i]) printf("%d ",i); 48 return 0; 49 }