题目链接:Vases and Flowers
大致题意
给定一个整数n, 表示有n个花瓶(初始为空花瓶), 编号从0~n-1. 有如下两种操作:
①从编号为x的花瓶开始, 要放y朵花, 从前往后一次遍历, 如果是空花瓶则放一朵花在里面, 直至放完所有花或者遍历到最后一个花瓶为止. 倘若此时还有花放不下, 则将它们直接丢弃.
②清理[l, r]区间的所有花瓶, 如果里面有花则将其丢弃
对于每个操作①, 需要输出第一个放花的位置和最后一个放花的位置. 倘若一朵花都放不下, 需要输出"Can not put any one."
对于每个操作②, 需要输出该区间被清理的花的数量
解题思路
线段树的区间修改和区间查询.
线段树维护当前区间内可以放花朵的数目.
解法一:
对于每个操作①而言, 首先用全局变量L, R维护第一个放花与最后一个放花的位置.
找到符合要求的区间后, 如果当前的区间可以放的花朵数量<=需要放置, 则将当前区间放满花朵, 同时查看当前区间第一个和最后一个空花瓶位置, 并更新L与R. 反之若当前区间不满足要求应当先递归左子树, 再递归右子树(因为要尽可能把前面的空花瓶都放满).
上述思路我们需要实现两个操作: 找到当前区间第一个空花瓶与最后一个空花瓶. 我们可以分别写两个函数来实现.
对于每个操作②而言, 就是简单的区间查询, 我们可以顺便进行花瓶清空操作.
解法二:
首先说操作②的实现: 我们的查询函数会返回[l, r]区间的空花瓶数目, 则操作②清理花的数目= [l, r]长度 - [l, r]空花瓶个数.
对于操作①: 我们其实可以通过查询[x, n]区间空花瓶的个数, 来判断是否一朵花都放不下. 如果可以放的下花的话, 我们可以采用二分的思路去二分[x, n]区间, 来得到第num个空花瓶所在的位置.
特别注意: 对于最后放花的位置: 如果可以放得下所有y朵花, 我们应查询第y个空花瓶的位置, 反之我们应当查询[x, n]区间最后一个空花瓶所在的位置.
AC代码
/* 解法一: 全局变量 */
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 5E4 + 10;
struct node {
int l, r;
int cou; //代表可以放花的数目
int lazy; //由于lazy = 0为一种修改标签, 所以本题lazy初值赋为-1
}t[N << 2];
void pushdown(node& op, int lazy) {
op.cou = lazy * (op.r - op.l + 1);
op.lazy = lazy;
}
void pushdown(int x) {
if (t[x].lazy == -1) return;
pushdown(t[x << 1], t[x].lazy), pushdown(t[x << 1 | 1], t[x].lazy);
t[x].lazy = -1;
}
void pushup(int x) { t[x].cou = t[x << 1].cou + t[x << 1 | 1].cou; }
void build(int l, int r, int x = 1) {
t[x] = { l, r, 1, -1 };
if (l == r) return;
int mid = l + r >> 1;
build(l, mid, x << 1), build(mid + 1, r, x << 1 | 1);
pushup(x);
}
int L, R;
int findleft(int x) { //找到当前最左侧可以放花的位置
if (t[x].l == t[x].r) return t[x].l;
pushdown(x);
return t[x << 1].cou ? findleft(x << 1) : findleft(x << 1 | 1); //左侧可以放花, 则递归左子树, 反之则递归右子树
}
int findright(int x) { //找到当前最右侧可以放花的位置
if (t[x].l == t[x].r) return t[x].l;
pushdown(x);
return t[x << 1 | 1].cou ? findright(x << 1 | 1) : findright(x << 1);
}
void modify(int l, int r, int& c, int x = 1) { //c记得传递引用类型, 代表要放花的数目
if (!c || !t[x].cou) return;
if (l <= t[x].l && r >= t[x].r) {
if (t[x].cou <= c) {
L = min(L, findleft(x)), R = max(R, findright(x)); //用全局变量L, R维护答案
c -= t[x].cou, pushdown(t[x], 0);
return;
}
if (t[x].l == t[x].r) return;
}
pushdown(x);
int mid = t[x].l + t[x].r >> 1;
if (l <= mid) modify(l, r, c, x << 1);
if (r > mid) modify(l, r, c, x << 1 | 1);
pushup(x);
}
int ask(int l, int r, int x = 1) {
if (l <= t[x].l && r >= t[x].r) {
int res = t[x].r - t[x].l + 1 - t[x].cou; //区间长度 - 可以放花的数目 = 已经放花的数目
pushdown(t[x], 1);
return res;
}
pushdown(x);
int mid = t[x].l + t[x].r >> 1;
int res = 0;
if (l <= mid) res += ask(l, r, x << 1);
if (r > mid) res += ask(l, r, x << 1 | 1);
pushup(x);
return res;
}
int main()
{
int T; cin >> T;
while (T--) {
int n, m; scanf("%d %d", &n, &m);
build(1, n); //建树区间还是习惯从1开始, 所以后续操作也要做相应的映射
while (m--) {
int op, x, y; scanf("%d %d %d", &op, &x, &y);
if (op == 1) {
L = n, R = 0;
int temp = y; //这里因为y要传递引用, 如果最后y值不变, 表示一朵花都没放.
modify(x + 1, n, y);
if (y != temp) printf("%d %d\n", L - 1, R - 1);
else printf("Can not put any one.\n");
}
else printf("%d\n", ask(x + 1, y + 1));
}
printf("\n");
}
return 0;
}
/* 解法二: 二分 */
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 5E4 + 10;
struct node {
int l, r;
int val;
int lazy;
}t[N << 2];
void pushdown(node& op, int lazy) {
op.val = lazy * (op.r - op.l + 1);
op.lazy = lazy;
}
void pushdown(int x) {
if (t[x].lazy == -1) return;
pushdown(t[x << 1], t[x].lazy), pushdown(t[x << 1 | 1], t[x].lazy);
t[x].lazy = -1;
}
void pushup(int x) { t[x].val = t[x << 1].val + t[x << 1 | 1].val; }
void build(int l, int r, int x = 1) {
t[x] = { l, r, 1, -1 };
if (l == r) return;
int mid = l + r >> 1;
build(l, mid, x << 1), build(mid + 1, r, x << 1 | 1);
pushup(x);
}
void modify(int l, int r, int c, int x = 1) {
if (l <= t[x].l && r >= t[x].r) {
pushdown(t[x], c);
return;
}
pushdown(x);
int mid = t[x].l + t[x].r >> 1;
if (l <= mid) modify(l, r, c, x << 1);
if (r > mid) modify(l, r, c, x << 1 | 1);
pushup(x);
}
int ask(int l, int r, int x = 1) { //查询[l, r]区间的空花瓶数目
if (l <= t[x].l && r >= t[x].r) return t[x].val;
pushdown(x);
int mid = t[x].l + t[x].r >> 1;
int res = 0;
if (l <= mid) res += ask(l, r, x << 1);
if (r > mid) res += ask(l, r, x << 1 | 1);
return res;
}
int n, m;
int getindex(int a, int c) { //找到[a, n]区间, 第c个可以放花瓶的位置
int l = a, r = n;
while (l < r) {
int mid = l + r >> 1;
if (ask(a, mid) >= c) r = mid;
else l = mid + 1;
}
return l;
}
int main()
{
int T; cin >> T;
while (T--) {
scanf("%d %d", &n, &m);
build(1, n);
while (m--) {
int k, x, y; scanf("%d %d %d", &k, &x, &y);
if (k == 1) {
int cou = ask(x + 1, n);
if (!cou) printf("Can not put any one.\n");
else {
int L = getindex(x + 1, 1);
int R = getindex(x + 1, min(y, cou)); //注意要和cou取一个min
modify(L, R, 0);
printf("%d %d\n", L - 1, R - 1);
}
}
else {
int res = y - x + 1 - ask(x + 1, y + 1);
modify(x + 1, y + 1, 1);
printf("%d\n", res);
}
}
puts("");
}
return 0;
}
一杯茶, 一包烟, 一个BUG改一天 TwT