1. 程式人生 > >2017ccpc哈爾濱 hdu 6231 B k-th number 題解 二分答案+尺取法

2017ccpc哈爾濱 hdu 6231 B k-th number 題解 二分答案+尺取法

Alice are given an array A[1..N]A[1..N] with NN numbers.

Now Alice want to build an array BB by a parameter KK as following rules:

Initially, the array B is empty. Consider each interval in array A. If the length of this interval is less than KK, then ignore this interval. Otherwise, find the KK-th largest number in this interval and add this number into array BB.

In fact Alice doesn’t care each element in the array B. She only wants to know the MM-th largest element in the array BB. Please help her to find this number.
Input
The first line is the number of test cases.

For each test case, the first line contains three positive numbers N(1≤N≤105),K(1≤K≤N),MN(1≤N≤105),K(1≤K≤N),M. The second line contains NN numbers Ai(1≤Ai≤109)Ai(1≤Ai≤109).

It’s guaranteed that M is not greater than the length of the array B.
Output
For each test case, output a single line containing the MM-th largest element in the array BB.
Sample Input
2
5 3 2
2 3 1 5 4
3 3 1
5 8 2
Sample Output
3
2
題意,t組資料,n,k,m,對於數列中每個連續的且長度大於k的區間,找出他的第k大數,然後在這些數中找出第m大的數。
做法:二分答案,然後難點變為怎麼統計答案作為第K大的區間個數(這裡說法是不嚴謹的)。首先做一個轉化,對於每一個答案,要使該答案滿足第m大,那麼說把該答案帶入到原數列中,肯定會有m個區間滿足該答案是第k大,如果多了(區間數大於m)說明該答案小了,否則說明該答案大了。以此作為二分依據,那麼該如何統計滿足條件的區間數呢?利用尺取法即可。定義兩個指標分別用來統計前面和後面的區間數。具體情況先看程式碼,然後我再用一組樣例來解釋。
code:

#include<iostream>
#include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
int N,K;
ll M;
int a[100005];
bool judge(int x){
    ll ans=0;//區間個數
    int num=0;//當前>x的數的個數
    int j=1;
    for(int i=1;i<=N;i++){
        if(a[i]>=x)
            num++;
        //只要之前有k個數大於等於自身,自身就可以作為本區間的第k大數
        if(num==K){
            ans+=N-i+1;//統計後面一共可以形成多少個區間
            while(a[j]<x){
                ans+=N-i+1;//統計前面一共可以形成多少個區間
                j++;
            }
            num--;//還原狀態
            j++;
        }
    }
  // cout<<ans<<endl;
    if(ans>=M)
        return true;
    else
        return false;

}


int main(){
    int t;
    cin>>t;
    while(t--){
        scanf("%d%d%lld",&N,&K,&M);

        for(int i=1;i<=N;i++)
            scanf("%d",&a[i]);
      //  int l=1,r=1000000000;
     int l  = *min_element(a+1,a+N+1);
     int r = *max_element(a+1,a+N+1);
        int m;
        //cout<<judge(1)<<" "<<judge(2)<<" "<<judge(3)<<endl;
        while(l<r){
            m=r-(r-l)/2;
           // cout<<"m"<<"=  "<<m<<" ";
            if(judge(m))
                l=m;
            else
                r=m-1;
        }
        printf("%d\n",l);

    }
    return 0;
}

下面圖片多打印出來的部分是本程式碼跑一遍,二分過程中的每個答案m
和 每個答案m跑出來的 滿足條件(即該答案代入數列,是第k大的數)的區間數是多少
這裡寫圖片描述

看這組資料                            
6 3 5           
2 3 1 5 4 1
滿足a[i]>=k的數的個數分別是  
1 2 2 3 4 4
該組資料共有10個區間
區間             第3大數
2 3 1              1
3 1 5              1    
1 5 4              1
5 4 1              1
2 3 1 5            2
3 1 5 4            3
1 5 4 1            1
2 3 1 5 4          3
3 1 5 4 1          3
2 3 1 5 4 1        3

此過程中二分到3,找出4個滿足條件的區間
二分到2 找出5個滿足條件的區間
這5個區間是怎麼找出來的呢
首先掃描過程中掃到5時,開始有了三個數,此時ans+=(6-4+1),即ans+=3;
這時相當於統計上了
2 3 1 5
2 3 1 5 4
2 3 1 5 4 1
這三個區間
注意後兩個區間本來第k大得數是3,但是這時候也統計上了,所以說其實這裡滿足條件的區間,到底滿足什麼條件呢?其實是不太好描述的,說是答案作為第K大的區間個數,其實不太對,應該是 答案及比答案大的數 做為區間第k大的 區間個數
計算式N-i+1,是統計5這個數連同它自己之後的區間,而前指標j這裡作為統計之前的區間由於並不滿足a[j]

int main(){
    int t;
    cin>>t;
    while(t--){
        scanf("%d%d%lld",&N,&K,&M);

        for(int i=1;i<=N;i++)
            scanf("%d",&a[i]);
       int l=1,r=1000000000;
        int m;
        while(l<r){
            m=(l+r)/2;
           // cout<<"m"<<"=  "<<m<<" ";
            if(judge(m))
                l=m+1;
            else
                r=m;
        }
        printf("%d\n",l-1);

    }

    return 0;
}

另一種寫法:

int main(){
    int t;
    cin>>t;
    while(t--){
        scanf("%d%d%lld",&N,&K,&M);

        for(int i=1;i<=N;i++)
            scanf("%d",&a[i]);
      //  int l=1,r=1000000000;
     int l  = *min_element(a+1,a+N+1);
     int r = *max_element(a+1,a+N+1);
        int m;
        //cout<<judge(1)<<" "<<judge(2)<<" "<<judge(3)<<endl;
        while(l<r){
            m=r-(r-l)/2;
           // cout<<"m"<<"=  "<<m<<" ";
            if(judge(m))
                l=m;
            else
                r=m-1;
        }
        printf("%d\n",l);

    }

    return 0;
}

這兩種寫法一種是適用於整數1到10000000這樣式的二分,所得答案為l-1,一種是更普遍的,對左右端點沒有要求,所的答案是l,但是對於取整方法卻有了更高的要求,即mid = r-(r-l)/2;

相關推薦

2017ccpc哈爾濱 hdu 6231 B k-th number 題解 二分答案+取法

Alice are given an array A[1..N]A[1..N] with NN numbers. Now Alice want to build an array BB by a parameter KK as following rul

B - K-th Number HDU - 6231[二分+取]

題意:給出n,m,k有n個數字,詢問這個陣列中所有區間第k大數字組成的陣列中第m大的數字。(好繞啊,希望能懂OAO) 題解:嘗試考慮一下我們如果已經知道一個數字之後,在陣列中大於有m以及多餘m個區間的第k大大於這個數字,那麼這個數字一定不可能是答案,根據這個我們可以確定了一個單

POJ2104:K-th Number——題解

nbsp dash str sdi targe ash sum queue sin http://poj.org/problem?id=2104 題目大意:求區間第k小。 ———————

6231 K-th Number2017CCPC哈爾濱二分+取法

Alice are given an array A[1..N] with N numbers. Now Alice want to build an array B by a parameter K as following rules: Initially, the array B is empty

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

lld name scrip string php doesn title frame review 題目鏈接 Problem Description Alice are given an array A[1..N] with N numbers.Now Alic

HDU 6231 K-th Number——二分+

m要開long long,太苟了 首先二分答案,對於二分到的一個數x,我們要判斷大於等於x的數做第k大的區間是否有m個,可以用尺取法,列舉每個左界,對於一個左界求一個右界,使得區間內大於等於x的數有k

6231 K-th Number 二分 + 取法(好題)

1.題意:給你n個數字的序列,讓你把任意一個連續區間內第k大的數字插入到陣列B裡面,最後求B中第m大的數字。 2.分析: (1)比賽到時候致命的一個錯誤就是:把第k大的數字理解反了。。2 3 1第三大 , 自以為是3了(應該是1)。。然後按照錯的題意 意*了各種做法。。而

6231 K-th Number

Alice are given an array A[1..N]A[1..N] with NN numbers.  Now Alice want to build an array BB by a parameter KK as following rules:  Initi

POJ 2104 K-th Number(區間第k大數)(平方切割,歸並樹,劃分樹)

ac代碼 deb rank turn tracking line 查看 div 能夠 題目鏈接: http://poj.org/problem?id=2104 解題思路: 由於查詢的個數m非常大。樸素的求法無法在規定時間內求解。因此應該選用合理的方式維護數據來做到高效

POJ 2104 K-th Number(主席樹)

ber sca first n) 次數 example == scan sorted K-th Number Time Limit: 20000MS Memory Limit: 65536K Total Submissions: 5742

POJ 2104 K-th Number (主席樹)

std +++ esp space ctype == string uniq upd 題意:給定一個序列,然後有 q 個詢問,每次詢問 l - r 區間內的第 k 大的值。 析:很明顯的主席樹,而且還是裸的主席樹,先進行離散化,然後用主席樹進行查詢就好。 代碼如下: #p

[POJ 2104]K-th Number

n) 劃分樹 tput lease lap form 我們 歸並 nts Description You are working for Macrohard company in data structures department. After failing your

K-th Number

nts include cnblogs number after push bits 方法 must K-th Number Time Limit: 20000MS Memory Limit: 65536K Total Submissions

POJ 2104 K-th Number

poj 2104 working lan 只需要 please lin absolut input nlogn Time Limit: 20000MS Memory Limit: 65536K Total Submissions: 5948

整體二分初識--POJ2104:K-th Number

只需要 區域 左右 gif max 技術 因此 space efi n<=100000個數有m<=5000個詢問,每次問區間第k大。 方法一:主席樹!…… 方法二:整體二分。 整體二分一次性計算半個值域對一個區間的詢問的貢獻,然後根據“這半邊的貢獻在某個詢問中可

POJ 2104 K-th Number ( 求取區間 K 大值 )

二分法 esp size 麻煩 == 平方分割 closed push_back ret 題意 : 給出一個含有 N 個數的序列,然後有 M 次問詢,每次問詢包含 ( L, R, K ) 要求你給出 L 到 R 這個區間的第 K 大是幾 分析 : 求取區間 K 大值是

poj2104 K-th Number

per scanf algo using clas urn space n) mat 思路: 平方分割。 實現: 1 #include <cstdio> 2 #include <algorithm> 3 #include <vector

Poj 2104 K-th Number 主席樹模版題

OS tdi pda sig signed begin ostream air color 題意:離線詢問[l,r]區間第k大 題解:模版題,入門題 #include <iostream> #include <cstdio> #inclu

POJ-2104 K-th Number CDQ分治

col 分享圖片 pri 找到 oid play out else mes 題目傳送門 題意:給你一個序列,長度為n,m次詢問,詢問一段區間的第k大。 題解:CDQ分治,對整個值域進行分治。每次取一個mid, 計算出整個區間內mid <= 的數目,如果 num >

【POJ 2104】【主席樹模板題】K-th Number

題意:       靜態詢問區間第K大問題。給出一個數組,然後多次詢問某一區間第K大數是多少。   思路:       典型的主席樹模板題。       所以就大致講一下靜態主席樹