ICPC2018南京网络赛 G. Lpl and Energy-saving Lamps(线段树)
-
题意:给出n个房间,每个房间有\(k_i\)个灯泡需要更换节能灯,每个月会买m个节能灯,从第1个房间开始遍历,若\(k_i\)小于或等于当前手中节能灯数量,则替换,否则跳到下个房间,直到所有房间灯泡都换完后则每个月不再购买节能灯,有\(q\)次询问,每次询问需要输出第\(x\)个月已经更换节能灯的房间数量和手中还剩余的节能灯数量。
-
思路:区间查询 + 单点修改的线段树。
-
解析:首先是需要预处理出每个月已经更换节能灯的房间数量和手中还剩余的节能灯数量,在预处理过程的每个月中,如果当前所有房间(线段树的根节点)中存在一个房间需要更换的节能灯数量\(k_i\)小于或等于当前手中节能灯数量,则通过线段树去找到第一个满足条件的房间,并将其需要换节能灯的数量设为INF,然后循环这样的操作,若不存在满足条件的房间则跳到下一个月,让手中的节能灯数量增加,直到所有的房间灯泡被换完为止。
-
代码:
#include<iostream> #include<cstdio> #include<algorithm> #define debug(x) cout << "***" << x << endl using namespace std; const int N = 1e5 + 5; const int inf = 0x3f3f3f3f; int k[N]; pair<int, int> d[N]; int n, m, q; int num = 0; //当前手中节能灯的数量 struct Node { int l, r; int minV; //区间最小值 }tr[N * 4]; void pushup(int u) { tr[u].minV = min(tr[u << 1].minV, tr[u << 1 | 1].minV); } void build(int u, int l, int r) { tr[u] = {l, r}; if(l == r) tr[u].minV = k[l]; else { int mid = l + r >> 1; build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r); pushup(u); } } void update(int u, int l, int r) { if(l == r) { num -= tr[u].minV; tr[u].minV = inf; //第l个房间已经被更换完毕,令其需要更换的数量为INF k[l] = inf; } else { int mid = tr[u].l + tr[u].r >> 1; if(tr[u << 1].minV <= num) update(u << 1, l, mid); //优先选择离起点近的房间 else update(u << 1 | 1, mid + 1, r); pushup(u); } } int main() { scanf("%d%d", &n, &m); for(int i = 1; i <= n; i++) scanf("%d", &k[i]); build(1, 1, n); //预处理 int cnt = 0; //已更换灯泡的房间数 for(int i = 1; i <= N - 5; i++) { if(cnt < n) num += m; //已经换完所有房间灯泡则每个月不再买灯泡 while(tr[1].minV <= num) //存在满足条件的房间 { cnt ++; update(1, 1, n); } d[i].first = cnt, d[i].second = num; //first: 已更换灯泡的房间数, second: 手中剩余灯泡数量 } scanf("%d", &q); while(q --) { int x; scanf("%d", &x); printf("%d %d\n", d[x].first, d[x].second); } return 0; }

浙公网安备 33010602011771号