[BZOJ3441]乌鸦喝水

题目链接BZOJ3441

分析
1. 乍一看, O(NM) 的暴力模拟,超时。
2. 首先,预处理出每个水缸够喝多少次,然后按照次数从小到大排序。
3. 设 cnt 为喝了多少次, cir 为喝了多少趟, pos 记录乌鸦的位置,初始为 0 ,用树状数组维护喝了若干次后,区间内还能喝的水缸数量。
4. 当处理第i个点时,若 i 的次数大于区间[pos+1,n]的可用水缸数量(设为 sum[pos+1,n] ),那么说明 i 可以撑到这一趟喝完,于是更新cnt cir pos=0 ,直到 cnt+sum[pos+1,n]>i 的剩余次数或者 cir>=m ;因为 i 的次数是当前最少的,所以若i可以撑完一趟,那么后面的水缸都可以。
5. 然后对于 i 剩下的次数,二分一个j,使 sum[pos+1,j] 等于 i 剩下的可用次数,即i可以撑到下一轮的 j 号点被喝的时候,然后更新pos=j
6. 因为水缸按可饮用次数排序,那么排在后面的水缸用光的时刻一定比前面的晚,于是 i+1 号水缸可以继承 i <script type="math/tex" id="MathJax-Element-3380">i</script>号水缸用完时乌鸦的状态。
7. 详细见代码。

上代码

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 10;

int n, m, L;
inline int read() {
    char ch;
    int ans = 0, neg = 1;
    while (ch = getchar(), ch < '0' || ch > '9')
        if (ch == '-') neg = -1;
    while (ch >= '0' && ch <= '9')
        ans = ans * 10 + ch - '0', ch = getchar();
    return ans * neg;
}

int cnt, pos, cir;
struct nodePoi {
    int id, val;
    nodePoi() {}
    nodePoi(int a, int b) : id(a), val(b) {}
    inline bool operator < (const nodePoi &a) const {
        return val < a.val;
    }
} poi[N];

int sum[N]; // 用树状数组维护区间内可用水缸数量
#define lowbit(a) (a & (-a))
int query(int a) {
    int ans = 0;
    for (int i = a; i; i -= lowbit(i)) ans += sum[i];
    return ans;
}
void modify(int a, int b) {
    for (int i = a; i <= n; i += lowbit(i)) sum[i] += b;
}
int findPlc(int a) { // 二分查找i在下一轮能撑到的时候
    int l = pos, r =  n;
    int mid, ans = l;
    while (l <= r) {
        mid = (l + r) >> 1;
        if  (query(mid) - query(pos) > a)
            r =  mid - 1;
        else l = mid + 1, ans = mid;
    }
    return ans;
}
int main() {
    n = read(), m = read(), L = read();
    for (int i = 1; i <= n; i++) poi[i] = nodePoi(i, read());
    for (int i = 1; i <= n; i++) {
        poi[i].val = (L - poi[i].val) / read() + 1;
        modify(i, 1);
    } sort(poi + 1, poi + n + 1);
    pos = cnt = cir = 0;
    for (int i = 1; i <= n; i++) {
        if (poi[i].val < cnt) {
            modify(poi[i].id, -1); continue;
        }
        int tmp;
        while (cir < m && (tmp = query(n) - query(pos)) + cnt <= poi[i].val)
            cnt += tmp, cir++, pos = 0; // 一趟一趟的更新
        if (cir >= m) break;
        pos = findPlc(poi[i].val - cnt), cnt = poi[i].val;
        modify(poi[i].id, -1); // 当前桶用完了
    }
    printf("%d\n", cnt);
    return 0;
}

以上

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值