【NOIP模拟】数字对(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想知道序列中所有特殊区间的最大价值是多少,而有多少个这样的区间呢?这些区间又分别是哪些呢?你能帮助她吧。

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

思路:

 1 #include<map>
 2 #include<set>
 3 #include<cmath>
 4 #include<cstdio>
 5 #include<vector>
 6 #include<cstring>
 7 #include<cstdlib>
 8 #include<iostream>
 9 #include<algorithm>
10 #define MAXN 510000
11 using namespace std;
12 
13 
14 int f[MAXN][20],g[MAXN][20];
15 int a[MAXN],b[MAXN],c[MAXN];
16 int n,num;
17 
18 int gcd(int x,int y)
19 {
20     int a,b,r,t;
21     a=x; b=y;
22     if(a<b){t=a;a=b;b=t;}
23     while(1)
24     {
25         r=a%b;
26         a=b;
27         b=r;
28         if(!r) break;
29         
30     }
31     return a;
32 } 
33 
34 int query1(int x,int y)
35 {
36   int len,l;
37   len=y-x+1; l=log(len)/log(2);
38   return gcd(f[x][l],f[y-(1<<l)+1][l]);
39 }
40 
41 int query2(int x,int y)
42 {
43   int len,l;
44   len=y-x+1; l=log(len)/log(2);
45   return min(g[x][l],g[y-(1<<l)+1][l]);
46 }
47 int isok(int k)
48 {
49     num=0;
50     for(int i=1;i+k-1<=n;i++)
51     {
52         int s1=query1(i,i+k-1);
53         int s2=query2(i,i+k-1);
54         if(s1==s2) b[++num]=i;
55     //    printf("%d %d %d %d\n",i,i+k-1,s1,s2);
56     }
57     if(num) return 1;
58      else return 0;
59 }
60 int main()
61 {
62     freopen("1.in","r",stdin);
63     freopen("1.out","w",stdout);
64     scanf("%d",&n);
65     for(int i=1;i<=n;i++) 
66     {
67         scanf("%d",&a[i]);
68         f[i][0]=g[i][0]=a[i];
69     }
70     int len=log(n)/log(2);
71     for(int i=1;i<=len;i++) 
72      for(int j=1;j+(1<<i)-1<=n;j++) 
73      {
74          f[j][i]=gcd(f[j][i-1],f[j+(1<<(i-1))][i-1]);
75          g[j][i]=min(g[j][i-1],g[j+(1<<(i-1))][i-1]);
76      }
77     int l,r,mid,last,ans;
78     l=1;r=n;last=ans=1;c[1]=1;
79     while(l<=r)
80     {
81         int mid=(l+r)/2;
82         if(isok(mid))
83         {
84             last=mid; ans=num;
85             for(int i=1;i<=num;i++) c[i]=b[i];
86             l=mid+1;
87         }
88          else r=mid-1;
89     }
90     printf("%d %d\n",ans,last-1);
91     for(int i=1;i<=ans-1;i++) printf("%d ",c[i]);    
92     printf("%d",c[ans]);
93 }

猜你喜欢

转载自www.cnblogs.com/myx12345/p/9270009.html