qduoj 218 签到题

Description

a坤和大明在一块由n个方块组成的棋盘(1 × n)上做游戏.一开始a坤在棋盘上放了k个矩形并且没有告诉大明具体位置.每个矩形都占a个连续方块(1 × a),任意两个矩形不可重叠且任意两个矩形至少间隔为1.保证没有矩形超出棋盘范围.之后大明开始进行射击,每当大明射向一个方块时,a坤都会告诉他打没打中.那么问题来了,a坤这个人素质有些问题,就很喜欢骗人,所以他总是告诉大明没打中.你要帮大明抓a坤的现行--a坤第一次可以被确认的撒谎发生在大明第几次射击.

Input

多组输入输出,第一行三个整数n,k,a(1 ≤ n, k, a ≤ 2·1e5),分别代表棋盘大小,矩形数量以及每个矩形的大小.保证能够把k个矩形(1 × a)全部按要求放入棋盘内(1 × n).第二行一个整数m,代表小明的射击次数.第三行m个整数p1,p2,...,pm(1 ≤ pi ≤ n),分别代表第i次的射击位置为pi.

Output

输出一个整数.如果能抓到a坤现行,就输出大明是在第几次可以确认a坤作弊的,否则输出-1.

Sample Input 1

11 3 3
5
4 8 6 1 11

Sample Output 1

3

Sample Input 2

5 1 3
2
1 5

Sample Output 2

-1

Sample Input 3

5 1 3
1
3

Sample Output 3

1

二分判断,对于给出的一些没射中的点,在他们之间可以放下多少个矩形,如果数量小于k就表示说谎啦,但是不能排着去查会超时,所以需要二分 ,找到小于k的最靠前位置。可以一开始先判断如果m个数都知道是否可以判断其说谎,如果可以展开二分,如果不可以直接-1.
set代码:
#include <iostream>
#include <cstdio>
#include <set>
using namespace std;
int n,k,a,m,d,s[200005];
int get(int t) {
    int last = 0,ans = 0;
    set<int> p;
    p.insert(n + 1);
    for(int i = 0;i <= t;i ++) {
        p.insert(s[i]);
    }
    for(set<int>::iterator it = p.begin();it != p.end();it ++) {
        ans += (*it - last) / (a + 1);
        last = *it;
    }
    return ans;
}
int main() {
    while(~scanf("%d%d%d",&n,&k,&a)) {
        scanf("%d",&m);
        for(int i = 0;i < m;i ++) {
            scanf("%d",&s[i]);
        }
        int l = 0,r = m - 1,mid;
        if(get(m - 1) >= k)l = -2;
        else
        while(l < r) {
            mid = (l + r) / 2;
            if(get(mid) >= k)l = mid + 1;
            else r = mid;
        }
        printf("%d
",l + 1);
    }
}

 排序代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int n,k,a,m,d,s[200005],s1[200005];
int get(int t) {///查看前t次给出的点  中间空隙可以放下几个矩形。
    int last = 0,ans = 0;
    for(int i = 0;i <= t;i ++) {
        s1[i] = s[i];
    }
    s1[t + 1] = n + 1;///作为最后的边界,这样可以查看所有的边界
    sort(s1,s1 + t + 1);///从小到大排序
    for(int i = 0;i <= t + 1;i ++) {
        ans += (s1[i] - last) / (a + 1);///除以a + 1是因为两个矩形之间间隔至少为1  而且s1[i] - last的值实际比间隔长度多1
        last = s1[i];///last是上个点 初始为0点
    }
    return ans;
}
int main() {
    while(~scanf("%d%d%d",&n,&k,&a)) {
        scanf("%d",&m);
        for(int i = 0;i < m;i ++) {
            scanf("%d",&s[i]);
        }
        int l = 0,r = m - 1,mid;
        if(get(m - 1) >= k)l = -2;///m个点之间可以放下k个矩形 就无法判断说谎
        else
        while(l < r) {///二分查找刚好放不下k个的时候
            mid = (l + r) / 2;
            if(get(mid) >= k)l = mid + 1;
            else r = mid;
        }
        printf("%d
",l + 1);
    }
}