hdu 6231 K-th Number(二分+尺取)

题目:点击打开链接

题意:

给一个长度为n的数组a,取一个子区间的第k大,区间长度小于k的区间忽略。把所有子区间的第k大放入b数组(可以重复放,所以b数组要比a数组大很多,所以不是要真的去模拟),求这个b数组的第m大。

思路:

显然答案是数组a中的一个数。

所以可以把a全放到b中然后排序,二分查找答案。

这个答案可以判断一下(尺取法):

答案设为x,cnt表示区间 [ l, r ]内大于等于x的个数。

当cnt

尺子右端向右移,r++;

当cnt>=k时,

从区间[l, r] 到 区间 [l, n - 1]的所有区间第 k 大都是大于等于 x 的

例 2 3 1 5 4 1 1和2 3 1 5 4  6 6    (l=0,r=5,其他同样例)

把这些区间加起来,sum+=(n-r)

如果sum>=m,可以保证这个x起码是第m大,但有可能是第m+1大,m+2大,

换句话说,符合样例一的区间有,2,3,1,5,4和3,1,5,4,第3大都是3,所以m=2时,答案为3,所以遍历到的x起码要有两个区间,

但有两个以上区间的x不一定是就是答案,

所以要再往右找到最贴近第m大的。

因为x=2时是包含x=3的,所以往右贴近。

#include
using namespace std;

typedef long long ll;
const int maxn=1e5+30;
int a[maxn],b[maxn];
ll n,m,k;

ll check(int x){
    int l=0,r=-1,cnt=0;
    ll sum=0;
    while(r=x)
                cnt++;
        }
        else{
            sum+=(n-r);
            if(a[l]>=x)
                cnt--;
            l++;
        }
    }
    return sum;
}

int main(){
    int T;
    cin>>T;
    while(T--){
        cin>>n>>k>>m;
        for(int i=0;i>a[i];
            b[i]=a[i];
        }
        sort(b,b+n);
        int l=0,r=n-1;
        ll ans;
        while(l<=r){
            int mid=(l+r)>>1;
            int t=b[mid];
            if(check(t)>=m){
                ans=t;  //最大的那个才恰好是答案
                l=mid+1;
            }
            else
                r=mid-1;;
        }
        cout<


你可能感兴趣的:(二分,尺取法)