题目链接:BZOJ3441
分析
1. 乍一看,
O(NM)
的暴力模拟,超时。
2. 首先,预处理出每个水缸够喝多少次,然后按照次数从小到大排序。
3. 设
cnt
为喝了多少次,
cir
为喝了多少趟,
pos
记录乌鸦的位置,初始为
0
,用树状数组维护喝了若干次后,区间内还能喝的水缸数量。
4. 当处理第
5. 然后对于
i
剩下的次数,二分一个
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;
}
以上