数字对——RMQ+二分答案

题目描述 
小H是个善于思考的学生,现在她又在思考一个有关序列的问题。

她的面前浮现出一个长度为n的序列{ai},她想找出一段区间[L, R](1 <= L <= R <= n)。

这个特殊区间满足,存在一个k(L <= k <= R),并且对于任意的i(L <= i <= R),ai都能被ak整除。这样的一个特殊区间 [L, R]价值为R - L。

小H想知道序列中所有特殊区间的最大价值是多少,而有多少个这样的区间呢?这些区间又分别是哪些呢?你能帮助她吧。

输入 
第一行,一个整数n.

第二行,n个整数,代表ai.

输出 
第一行两个整数,num和val,表示价值最大的特殊区间的个数以及最大价值。

第二行num个整数,按升序输出每个价值最大的特殊区间的L.

样例输入1 

4 6 9 3 6 
样例输出1 
1 3 

样例输入2 

2 3 5 7 11 
样例输出2 
5 0 
1 2 3 4 5

数据范围

30%: 1 <= n <= 30 , 1 <= ai <= 32.

60%: 1 <= n <= 3000 , 1 <= ai <= 1024.

80%: 1 <= n <= 300000 , 1 <= ai <= 1048576.

100%: 1 <= n <= 500000 , 1 <= ai < 2 ^ 31.

这道题主要求得是最长区间长度,可以发现它是具有单调性的:对于一段区间,如果它是特殊区间,那么它一定有子区间是特殊区间。因此我们可以二分最长区间长度,

对于每个二分出来的答案进行验证。那么怎么验证?可以发现特殊区间有几个很好的性质:1、ak一定是这段区间的最小值,因为如果有一个数ai比ak小,那ai一定不能被ak整除。2、ak一定是区间gcd,因为将所有ai都除以ak后,这段区间就变成了1,b1,b2……这些数gcd显然是1,再乘回ak后gcd就是ak了。那么只要比较一段区间最小值和gcd是否相同就能判断是否是特殊区间了。但如果暴力找区间最小值和区间gcd显然是不行的,因此要用ST表预处理出来g[i][j]和m[i][j]分别表示以j为起点往后2^i个数的gcd和最小值。再对于每个答案O(n)遍历所有起点判断并记录下来每个特殊区间的起点就OK了。每次验证是O(nlogn),总时间复杂度是O(n*(logn)^2)。

最后附上代码。

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<iostream>
  5 #include<cmath>
  6 using namespace std;
  7 int g[20][500010];
  8 int m[20][500010];
  9 int a[500010];
 10 int l[500010];
 11 int n;
 12 int L,R;
 13 int ans;
 14 int cnt;
 15 int q[500010];
 16 int flag;
 17 int gcd(int x,int y)
 18 {   
 19     if(x<y)
 20     {
 21         swap(x,y);
 22     }
 23     if(y!=0)
 24     {
 25         return gcd(y,x%y);
 26     }
 27     else
 28     {
 29         return x;
 30     }
 31 }
 32 bool check(int x)
 33 {
 34     cnt=0;
 35     for(int i=1;i+x-1<=n;i++)
 36     {
 37         int s=l[x];
 38         if(gcd(g[s][i],g[s][i+x-(1<<s)])==min(m[s][i],m[s][i+x-(1<<s)]))
 39         {
 40             q[++cnt]=i;
 41         }
 42     }
 43     if(cnt!=0)
 44     {
 45         ans=max(ans,x);
 46         return true;
 47     }
 48     return false;
 49 }
 50 int main()
 51 {
 52     scanf("%d",&n);
 53     for(int i=1;i<=n;i++)
 54     {
 55         scanf("%d",&a[i]);
 56         g[0][i]=a[i];
 57         m[0][i]=a[i];
 58     }
 59     for(int i=1;(1<<i)<=n;i++)
 60     {
 61         for(int j=1;j+(1<<(i-1))<=n;j++)
 62         {
 63             g[i][j]=gcd(g[i-1][j],g[i-1][j+(1<<(i-1))]);
 64             m[i][j]=min(m[i-1][j],m[i-1][j+(1<<(i-1))]);
 65         }
 66     }
 67     for(int i=2;i<=n;i++)
 68     {
 69         l[i]=l[i>>1]+1;
 70     }
 71     L=1;
 72     R=n;
 73     while(L<R-1)
 74     {
 75         int mid=(L+R)>>1;
 76         if(check(mid)==true)
 77         {
 78             L=mid;
 79         }
 80         else
 81         {
 82             R=mid;
 83         }
 84     }
 85     bool o1=check(L);
 86     bool o2=check(R);
 87     bool o3=check(ans);
 88     printf("%d %d\n",cnt,ans-1);
 89     for(int i=1;i<=cnt;i++)
 90     {
 91         if(flag==0)
 92         {
 93             printf("%d",q[i]);
 94             flag=1;
 95         }
 96         else
 97         {
 98             printf(" %d",q[i]);
 99         }
100     }
101 }

猜你喜欢

转载自www.cnblogs.com/Khada-Jhin/p/9118631.html
今日推荐