codeforces898D Alarmclock

来源:互联网 发布:对啊网网络销售好做么 编辑:程序博客网 时间:2024/05/16 09:11

有些类似线段覆盖的贪心。蛮巧妙的。


题意大概是这样的,
n个闹钟,第i个会在第ai时刻开始叫m分钟,如果在某一段区间[L,L+m]有超过k个闹钟响小明就会被吵醒,求最少关掉多少闹钟会使得小明不被吵醒,并且在区间外开始响的闹钟不计入区间中。
1kn2105, 1ai,m106
其实我第一反应是有些惊讶,毕竟ai竟然不是小于109。想到了什么?没错,值域思想。


先来理性(玄学)分析一波。一个直观的想法是,我们按照重叠最多的顺序排序,但是这样是有问题的,毕竟如果这么一排,就会导致左右两边分开,反而会使情况更糟。
但是如果我们从左往右扫就不一样了。只要碰到会吵醒的情况,我们就弹出,这样能够保证区间是连续的。并且,由于区间一定会出现问题,所以我们不能就此放置不管!那关哪个呢?我们选择最右面的,因为这样会让剩余的能关的更少。
而实现也以此分成两大派别。


(伪)滑动窗口派

简洁而明了的实现,类似滑动窗口滚一遍。

#include <iostream>#include <cstdio>#include <algorithm>#include <deque>using namespace std;int n, m, k, a[200003], cnt;int main() {    ios::sync_with_stdio(0);    cin.tie(0); cout.tie(0);    cin>>n>>m>>k;    for(int i = 1; i <= n; ++i) cin>>a[i];    sort(a + 1, a + n + 1);    deque<int> dq;    for(int i = 1; i <= n; ++i) {        while(dq.size() && dq.front() <= a[i] - m) dq.pop_front();        dq.push_back(a[i]);        while(dq.size() >= k) ++cnt, dq.pop_back();     }    cout<<cnt<<endl;    return 0;}

由于双端队列末尾恰好是最右面的,所以可以直接弹走。


BIT派
之前就提到过值域思想,那么我们可以将值域维护成一个差分数组,然后利用树状数组实现前缀和的查询和修改。

#include <iostream>#include <cstdio>#define N 1000003using namespace std;int n, m, k;int bit[N];inline int lowbit(int x) { return x & -x; }inline void update(int x, int val) {     while(x < N) {        bit[x] += val;        x += lowbit(x);    } }inline int query(int x) {    int ans = 0;    while(x) {        ans += bit[x];        x -= lowbit(x);    }    return ans;}inline int getsum(int l, int r) {    return query(r) - query(l);}int main() {    ios::sync_with_stdio(0);    cin.tie(0); cout.tie(0);    cin>>n>>m>>k;    int tmp = 0, ans = 0;    for(int i = 1; i <= n; ++i) cin>>tmp, update(tmp, 1);    for(int i = 0; i < N; ++i) {        if(i < m && getsum(0, i) == k) update(i, -1), ans++;        if(i >= m && getsum(i - m, i) == k) update(i, -1), ans++;    }    cout<<ans<<endl;    return 0;}

参考代码:
http://codeforces.com/contest/898/submission/33296415
http://codeforces.com/contest/898/submission/33292393

原创粉丝点击