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 }

 

posted @ 2017-11-22 19:09  MSPqwq  阅读(276)  评论(0编辑  收藏  举报