HDU 6231 (二分+双指针)
题意:给一个长度为n的数组,问在由这个数组的所有的区间第k小组成B数组中,第m大元素是多少
解法:这题较难的地方在于转化思维。如果去求所有区间的第k小,最坏复杂度是O(n*n)肯定超时。
这题正确的解法是二分一个最大的x,这个x满足有大于等于m个【区间的第k小】大于等于x.。
所以关键在于,如何求有多少个区间的第k小大于等于x.
一个区间第k小要大于等于x,则这个区间至少要有k个数大于等于x..
我们枚举区间的左端点L。对于每个左端L,可以找一个最小的r使得,当右端点大于等于r时,[L,r]有k个数大于等于x。所以L为左端点的区间中满足要求的区间数有 n-r+1个
而这个r关于l显然是有单调性的,所以可以考虑用双指针O(n)求出所有r.。
所以算法复杂度O(nlogn)
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<time.h> 4 #include<algorithm> 5 #include<vector> 6 #include<set> 7 #define ll long long 8 using namespace std; 9 int a[1000005],b[1000005]; 10 long long num; 11 int check(int n,int k,int m) 12 { 13 long long sum=0; 14 int i,j=0,s=0; 15 for(i=1;i<=n;i++) 16 { 17 while(s<k&&j<=n) 18 { 19 j++; 20 if(a[j]>=m) 21 s++; 22 } 23 if(j>n) 24 break; 25 sum+=n-j+1; 26 if(a[i]>=m) 27 s--; 28 } 29 // printf("%d %d\n",m,sum); 30 return sum<num; 31 } 32 void find(int n,int k) 33 { 34 int l=1,r=n+1,m; 35 while(l+1<r) 36 { 37 m=(l+r)/2; 38 if(check(n,k,b[m])) 39 r=m; 40 else 41 l=m; 42 } 43 printf("%d\n",b[l]); 44 } 45 int main() 46 { 47 int i,j,n,t,m,k,l,r; 48 scanf("%d",&t); 49 while(t--) 50 { 51 scanf("%d%d%I64d",&n,&k,&num); 52 for(i=1;i<=n;i++) 53 { 54 scanf("%d",&a[i]); 55 b[i]=a[i]; 56 } 57 sort(b+1,b+n+1); 58 find(n,k); 59 } 60 return 0; 61 }