计蒜客A1953 Lpl and Energy-saving Lamps
标签
- 2018icpc南京站网络赛G题
- 模拟
- 线段树
简明题意
- 给一个序列a[i],指第i个房间要安装的灯的数量。每个月你都可以得到m展灯,然后每个月你都需要去给房间装灯,装灯的规则是这样的:一次访问每一个房间,如果一个房间需要装的灯数小于你手上拥有的灯,你就可以给它装了,否则你就把手上的灯留到下一个月。会给q个询问,每个询问问你第i个月末,你成功给几个房间装上了灯,此时你手上的灯的数量是多少。
思路
- 实际上是一个模拟了,依次考虑每一个月,设手上的灯的数量是k,然后去找到第一个<=k的房间,让这个房间的值变成+无穷,然后再找第二个,知道找不到,然后把灯留到的二个月。
- 这里用线段树去维护序列中第一个<=k的房间的编号以及值
注意事项
- 注意到一旦装满灯,就不会再买新的灯了
总结
- 如果要维护序列中第一个<=k的值,并且带修,可以考虑线段树
AC代码
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 1e5 + 10;
int n, m, a[maxn];
struct Node
{
int l, r, min;
int min_id;
};
Node tree[maxn * 4];
void update(int o)
{
if (tree[o].l != tree[o].r)
{
tree[o].min = min(tree[o * 2].min, tree[o * 2 + 1].min);
/*if (tree[o * 2].min < tree[o * 2 + 1].min)
tree[o].min = tree[o * 2].min, tree[o].min_id = tree[o * 2].min_id;
else
tree[o].min = tree[o * 2 + 1].min, tree[o].min_id = tree[o * 2 + 1].min_id;*/
}
}
void build(int o, int l, int r)
{
tree[o].l = l, tree[o].r = r;
if (l == r)
{
tree[o].min = a[l];
return;
}
int mid = (l + r) / 2;
build(o * 2, l, mid);
build(o * 2 + 1, mid + 1, r);
update(o);
}
pair<int, int> ask(int o, int k)//询问最小权对应的id
{
if (tree[o].l == tree[o].r)
return pair<int, int>(tree[o].min, tree[o].l);
if (tree[o * 2].min <= k)
return ask(o * 2, k);
else if (tree[o * 2 + 1].min <= k)
return ask(o * 2 + 1, k);
else
return pair<int, int>(-1, -1);
}
void change(int o, int x)
{
if (tree[o].l == tree[o].r)
{
tree[o].min = 2e9;
return;
}
int mid = (tree[o].l + tree[o].r) / 2;
if (x <= mid)
change(o * 2, x);
else
change(o * 2 + 1, x);
update(o);
}
pair<int, int> ans[maxn];
void solve()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
build(1, 1, n);
int q;
scanf("%d", &q);
int max_month = -1e9;
vector<int> query;
while (q--)
{
int t;
scanf("%d", &t);
max_month = max(max_month, t);
query.push_back(t);
}
int ok_num = 0;
int k = 0;//当前手上的灯的数量
for (int i = 1; i <= max_month; i++)
{
if (ok_num != n)
k += m;
int cnt = 0;
while (1)
{
pair<int, int> temp = ask(1, k);
if (temp.first == -1)//找不到
break;
change(1, temp.second);
ok_num++;
int left = k - temp.first;//first是装的灯数,left是当前剩余的灯
k = left;
cnt++;
if (k == 0)
break;
}
ans[i].first = cnt + ans[i - 1].first, ans[i].second = k;
}
for (auto& it : query)
printf("%d %d\n", ans[it].first, ans[it].second);
}
int main()
{
freopen("Testin.txt", "r", stdin);
solve();
return 0;
}
双倍经验
- 无