ACM-ICPC 2018 南京赛区网络预赛 G. Lpl and Energy-saving Lamps
题目
有 n 个房间需要换灯泡,每天开始时会买 m 个新灯泡,从 1 ~ n 房间看,如果这个房间灯泡数量小于新灯泡,就把这个房间灯泡全部换新的,相应的新灯泡也要减去对应数量,每天经过一轮 1 ~ n,剩下的新灯泡会加到第二天。
给你 q 次询问,问你在第 i 天换了几个房间,剩余多少灯泡。
分析
首先每天换灯泡是要从左往右找第一个小于等于新灯泡数量的房间。
找到后更新线段树。
直到找不到这样的房间,然后进入下一天。
用线段树维护区间最小值,每次查询优先左区间,左区间没有再右区间。
然后模拟每天的查询。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define INF 0x3f3f3f3f
#define pii pair<int, int>
#define mk(x, y) make_pair(x, y)
#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
const int N = 1e5 + 10;
const int M = 1e2 + 10;
int n, m, q;
int a[N], ans[N], res[N];
int tr[N << 2]; // 维护区间最小值
ll sum;
void build(int l, int r, int rt){
if(l == r){
tr[rt] = a[l];
return;
}
int m = l + r >> 1;
build(lson);
build(rson);
tr[rt] = min(tr[rt << 1], tr[rt << 1 | 1]); // pushup(rt);
}
void update(int l, int r, int rt, int pos){
if(l == r){
tr[rt] = INF;
return;
}
int m = l + r >> 1;
if(pos <= m)
update(lson, pos);
else
update(rson, pos);
tr[rt] = min(tr[rt << 1], tr[rt << 1 | 1]); // pushup(rt);
}
int query(int l, int r, int rt, int x){
if(l == r){
res[x]++; // 换这个房间灯泡
sum -= tr[rt];
update(1, n, 1, l); // 更新线段树
return 1;
}
int m = l + r >> 1;
if(tr[rt<<1] <= sum) // 如果左区间最小值 小于当前灯泡值
return query(lson, x); // 优先询问左区间,左区间没有再右区间
else {
if(tr[rt<<1|1] <= sum)
return query(rson, x);
else {
return 0; // 都不行返回
}
}
}
void solve(){
int flag;
for(int i = 1; i < N; i++){
sum += m; // 月初买 m 灯泡
res[i] = res[i - 1];
if(res[i-1] == n){ // 所有房间换好灯泡
for(int j = i; j < N; j++){
res[j] = n;
ans[j] = ans[j - 1];
}
return;
}
while(query(1, n, 1, i)) ; // 直到找不到能换的房间
ans[i] = sum; // 每次的剩余
}
}
int main(){
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++){
scanf("%d", &a[i]);
}
build(1, n, 1); // 建树
solve(); // 预处理
scanf("%d", &q);
for(int i = 1, x; i <= q; i++){ // O(1) 查询
scanf("%d", &x);
printf("%d %d\n", res[x], ans[x]);
}
return 0;
}
/*
5 4
3 10 5 2 7
8
1 2 3 4 5 6 7 8
*/