标签:
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 }
标签:
原文地址:http://www.cnblogs.com/xcw0754/p/4506423.html