Vases and Flowers - HDU 4614 - Virtual Judge
题意不再赘述.
解题:
对于操作1:从a点开始插花,直到插花完成或者插不完了
011010010
首先,这个过程绝不可能模拟去完成,只需要想一想就知道时间复杂度是O(N),比如上面的01序列,0表示可以插花,1表示不能插花。
思维了一圈,我们发现只要确定插花的一个区间就可以了
比如从a点开始插花那么我们插花的区间设为[L,R],(L>=a)
所以 要得出L,R 既第一个插花位置和最后一个插花位置
由此 引出我们的重点 二分
为什么可以二分?
首先我们先认为线段树维护的S表示区间内可以插花的花瓶的数量
原因是线段树是二叉树,天生就适合二分,并且线段树维护的区间和S具有单调性
所以 使用二分找到第一个插花位置和最后一个插花位置
思想:在a点开始 往后插花 第一个位置的S应该满足什么样的性质?
应该满足[a,L]插满了花 如果S表示(可以插花的数量) 那么S应该>=1,表示至少要有一个空位置用来插花 如果S表示(已经插了花的花瓶) 那么(L-a+1)-S>=1 只不过S表示(可以插花的数量) 更为方便
再思想 最后一个位置插花的S应该满足什么样的性质?
假如在[L,R]插花K朵,最后一个位置的S必须满足S>=K 既这个区间空位置数量大于等于K
其他细节见代码给出了详细的解释
#include<iostream>
#define lc p<<1
#define rc p<<1|1
using namespace std;
const int N = 50003;
struct Node{
//我建议你不要在线段树里开l,r
//因为占空间比较大
int s, lazy;
}t[N<<2]; int n, m;
//s:没插花的数量
//lazy:题目设计到插花 拔花
//初始化lazy=-1表示什么也不做
//lazy=1表示拔花 把区间的空位S置为区间长度
//lazy=0表示插花 把区间的空位S置为0
void pushdown(int p,int l,int r) {
if (t[p].lazy == -1) return;
int mid = (l + r) >> 1;
t[lc].s = (mid - l + 1) * t[p].lazy;
t[rc].s = (r - mid) * t[p].lazy;
t[lc].lazy = t[rc].lazy = t[p].lazy;
t[p].lazy = -1;
}
void pushup(int p) {
t[p].s = t[lc].s + t[rc].s;
}
void build(int p, int l, int r) {
t[p] = { 1,-1 };
if (l == r)return;
int m = l + r >> 1;
build(lc, l, m); build(rc, m + 1, r);
pushup(p);
}
//L,R 表示区间修改区间
//l,r 表示节点区间
void update(int p, int L, int R, int l, int r, int k) {
if (L > r || R < l) return;
if (L <= l && r <= R) {
//这里的处理比较通用 k=1表示拔花 把空位S置为区间长度
//k=0表示插花 S=0表示区间空位置=0
t[p].s = (r - l + 1)*k;
t[p].lazy = k;
return;
}
pushdown(p, l, r);
int m = l + r >> 1;
update(lc, L, R, l, m, k);
update(rc, L, R, m + 1, r, k);
pushup(p);
}
//L,R 表示区间查询区间
//l,r 表示节点区间
int query(int p, int L, int R, int l, int r) {
if (L > r || R < l) return 0;
if (L <= l && r <= R) {
return t[p].s;
}
pushdown(p, l, r);
int m = l + r >> 1;
int sum = 0;
sum+=query(lc, L, R, l, m);
sum+=query(rc, L, R, m + 1, r);
return sum;
}
int binarysearch(int L, int R, int target) {
//在[L,R]二分得出第一朵花位置和最后一朵花位置
int l = L, r = R; int res = 0;
while (l <= r) {
int mid = (l + r) >> 1;
//---L------M------R-----
int t = query(1, L, mid, 1, n);//查询[L,M]的空位置
if (t >= target) {//空位置数量足够
res = mid;
r = mid-1;
}
else {
l = mid + 1;
}
}
return res;
}
void solve() {
cin >> n >> m;
build(1, 1, n);
for (int i = 1; i <= m; i++) {
int op; cin >> op;
if (op == 1) {
//a:起始位置
//f:插花数量
int a, f; cin >> a >> f;
//a++是因为题目下标是[0,N-1] 处理不方便
a++;
//首先查询[a,n]的空位置数量
int cnt = query(1, a, n, 1, n);
if (cnt == 0) {
cout << "Can not put any one." << endl;
}
else {
//题意:从a位置开始插花 插不完就扔掉
//只有cnt个空位置 f朵花
//肯定取最小值插花
f = min(f, cnt);
//s:[a,n]范围内第一朵花
//e:[a,n]范围内最后一朵花
int s = binarysearch(a, n, 1);
int e = binarysearch(a, n, f);
cout << s - 1 << ' ' << e - 1 << endl;
//把区间空位置修改为0
update(1, s, e, 1, n, 0);
}
}
else {
int a, b; cin >> a >> b;
a++; b++;
//query返回的是空位置数量
//区间长度减去空位置数量就是花的数量
cout << (b-a+1) - query(1, a, b, 1, n) << endl;
//把区间空位置为长度
update(1, a, b, 1, n, 1);
}
}
cout << endl;
}
int main() {
int t; cin >> t;
while (t--)
solve();
}