hihoCoder #1068 : RMQ-ST算法

AC G++ 826ms 146MB

 

 

思路:

  按照提示一的方法进行实现。使用二进制的思想,只需要找到从第i个开始的2^j个数中的最小。i=(1,n)而因为n上限为100万,100万的二进制(1111 01000010 01000000),那么j 最大不超过20。按最坏情况算,只需要计算n*20个结果就行了,也就是O(n)。

  重点在如何在O(1)时间内计算pre[i][2^j]?利用二进制的性质,假如要计算pre[2][5](即长度为4),如果已经知道了pre[2][2]和pre[4][2],那么pre[2][5]=min(pre[2][2], pre[4][2]),两者刚好覆盖了从2~5。那么只需要按照j从小到大的顺序来计算每个i就行了。

  空间需要n*n?不需要了!第二维的2^j中的j不会超过20,那么二维中的3 5 6 7 9 10....这些都是没有记录东西的,很浪费空间。那么可以用j来作为二维索引,则第二维只需要20以上就行了。

  时间复杂度O(n+q)。这样算应该对吧?

 

 1 #include <bits/stdc++.h>

 2 using namespace std;

 3 const int N=1000100;

 4 int w[N], pre[N][32], n, q;

 5 void pre_cal()

 6 {

 7     for(int i=0; i<n; i++)    pre[i][0]=w[i];

 8     for(int i=2,q=1; i<=n; i*=2,q++)

 9         for(int j=0; j<n; j++)

10             if(j+i-1<n)    pre[j][q]=min(pre[j][q-1],pre[j+i/2][q-1]);

11             else    break;

12 }

13 int main()

14 {

15     //freopen("input.txt", "r", stdin);

16     cin>>n;

17     for(int i=0; i<n; i++)    scanf("%d",&w[i]);    //输入重量

18     pre_cal();    //预处理

19 

20     cin>>q;

21     int L, R;

22     for(int i=0; i<q; i++)  //输入查询,并直接处理输出

23     {

24         scanf("%d%d", &L, &R);

25         int len=R-L+1, tmp=0, cnt=0;

26 

27         for(int i=0; i<30; i++)//找出二进制最高位的1

28         {

29             if(!(len>>i))

30             {

31                 tmp=((len>>(i-1))<<(i-1));

32                 break;

33             }

34             cnt++;

35         }

36         if(R==L)    printf("%d\n",w[L-1]);

37         else if(tmp==len)    printf("%d\n",pre[L-1][cnt-1]);//这步其实可省。

38         else  printf("%d\n",min(pre[L-1][cnt-1], pre[R-tmp][cnt-1]));

39     }

40     return 0;

41 }
AC代码

 

你可能感兴趣的:(code)