题目意思
第一行给两个整数n m,n表示房间数,m表示每个月购买的的灯泡数,随后给出n个数,表示每个房间的灯泡数(每个灯泡只需要更换一次),每个月从第一个房间遍历到第n个房间,当该房间的灯泡数小于等于现在有的灯泡数时,更新该房间的灯泡(只更新一次)。
然后给出q次询问,问你在某一个月时,已经更换灯泡的房间数和现在剩余的灯泡。(当所有房间灯泡都更换后,不再买入新的灯泡。)
样例输入
5 4
3 10 5 2 7
10
5 1 4 8 7 2 3 6 4 7
样例输出
4 0
1 1
3 6
5 1
5 1
2 0
3 2
4 4
3 6
5 1
解题思路
线段树维护区间最小值,当总区间的最小值大于当前灯泡数就进入下一个灯泡,否则一直寻找小于当前灯泡的最前面的数字。
AC代码
#include <iostream>
#include <algorithm>
#include <climits>
using ll = long long;
using namespace std;
struct node
{
int l;
int r;
ll min;
}que[200005];
struct Node
{
ll m;
ll t;
}ans[200005];
ll cnt = 0, tot = 0, val;
void push_up(int k)
{
que[k].min = min(que[k << 1].min, que[k << 1 | 1].min);
}
void build(int left, int right, int k)
{
que[k].l = left;
que[k].r = right;
que[k].min = 0;
if (left == right)
{
scanf("%lld", &que[k].min);
return;
}
int mid = (left + right) >> 1;
build(left, mid, k << 1);
build(mid + 1, right, k << 1 | 1);
push_up(k);
}
void query(int left, int right, int k)
{
if (left == right)
{
val = que[k].min;
que[k].min = LLONG_MAX;
return;
}
int mid = (left + right) >> 1;
if (tot >= que[k << 1].min)//如果当前区间的左儿子最小值小于等于手中灯泡数,找左边
query(left, mid, k << 1);
else//反之,找右边
query(mid + 1, right, k << 1 | 1);
push_up(k);
}
int main()
{
int n, m, t, maxn = 0;
int q[100005];
scanf("%d%d", &n, &m);
build(1, n, 1);
scanf("%d", &t);
for (int i = 0; i < t; i++)
{
scanf("%d", &q[i]);
maxn = max(maxn, q[i]);
}
for (int i = 1; i <= maxn; i++)
{
if (cnt != n)//当所有房间灯泡没有被全部更新
tot += m;
while (true)
{
if (que[1].min > tot)
break;
query(1, n, 1);
tot -= val;
cnt++;
}
ans[i].m = cnt;//记录每一个月份对应的答案
ans[i].t = tot;
}
for (int i = 0; i < t; i++)
printf("%lld %lld\n", ans[q[i]].m, ans[q[i]].t);
}