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
",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
",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 }
Ac代码
原文地址:https://www.cnblogs.com/qswg/p/7820446.html