[BZOJ3441]乌鸦喝水

来源:互联网 发布:淘宝小号怎么申请 编辑:程序博客网 时间:2024/04/28 12:25

题目链接:BZOJ3441

分析
1. 乍一看,O(NM)的暴力模拟,超时。
2. 首先,预处理出每个水缸够喝多少次,然后按照次数从小到大排序。
3. 设cnt为喝了多少次,cir为喝了多少趟,pos记录乌鸦的位置,初始为0,用树状数组维护喝了若干次后,区间内还能喝的水缸数量。
4. 当处理第i个点时,若i的次数大于区间[pos+1,n]的可用水缸数量(设为sum[pos+1,n]),那么说明i可以撑到这一趟喝完,于是更新cntcirpos=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号水缸用完时乌鸦的状态。
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;}

以上

0 0
原创粉丝点击