原题地址
整个题目抽象下来就是说给一个长度为n的初始全为0的串,然后接下来有m个操作,操作1是从第a个位置开始,一直到n个位置,将b个0变成1,如果从a开始没有0存在输出“Can not put any one.",否则就进行变更(变更的个数可以小于b)并且输出变更的区间。操作2是问a到b位置之间有多少个1,并且将a到b位置全部变为0。
这个题目和上次做过的那个摧毁村庄的题目很像,当时那个题目是问连续的村庄有多少个,那样要用线段树维护左边连续和右边连续的最大值,或者用树状数组直接二分就可以解决,这个题目的限制要小一些,反而更加难处理,常规线段树是可以维护一个区间和的(要用lazy标记防止超时),那么我们想怎么样就使用常规线段树来解决这个问题。首先线段树的查询操作需要一个区间,这正好对应第二种操作,比较简单。而第一种操作,我们想到,线段树的区间修改也需要一个区间,那么问题就变成了我们要在(a, n)这个区间上找到一个区间(l, r),使得(l, r)的区间和正好等于所要变更的b和(a, n)上所剩下的0的最小值,因此我们想到了二分,我们先在(a , n)上找到第一个满足位置是0的l,然后再在(a, n)上找到第一个满足(a, r)上有b个0的r,这样我们就找到了我们要修改的区间,输出这个区间(注意原题是按照0~n-1编号的)并修改这个区间上的串。
#include
#include
using namespace std;
const static int maxn = 5e4+10;
int n, m;
struct segmentree
{
int l, r;
int lazy;
int sum;
}st[maxn*4];
void pushup(int rt)
{
st[rt].sum = st[rt<<1].sum + st[rt<<1|1].sum;
}
void pushdown(int rt)
{
if(~st[rt].lazy)
{
st[rt<<1].lazy = st[rt<<1|1].lazy = st[rt].lazy;
int mid = (st[rt].l + st[rt].r) >> 1;
st[rt<<1].sum = (mid - st[rt].l + 1) * st[rt<<1].lazy;
st[rt<<1|1].sum = (st[rt].r - mid) * st[rt<<1|1].lazy;
st[rt].lazy = -1;
}
}
void buildtree(int l, int r, int rt)
{
st[rt].l = l;
st[rt].r = r;
st[rt].lazy = -1;
if(l == r)
{
st[rt].sum = 1;
return;
}
int mid = (l + r) >> 1;
buildtree(l, mid, rt<<1);
buildtree(mid+1, r, rt<<1|1);
pushup(rt);
}
int query(int L, int R, int rt)
{
if(st[rt].l >= L && st[rt].r <= R)
{
return st[rt].sum;
}
pushdown(rt);
int mid = (st[rt].l + st[rt].r) >> 1;
int ans = 0;
if(mid >= L)
ans += query(L, R, rt<<1);
if(mid < R)
ans += query(L, R, rt<<1|1);
return ans;
}
void update(int L, int R, int rt, int c)
{
if(st[rt].l >= L && st[rt].r <= R)
{
st[rt].sum = (st[rt].r - st[rt].l + 1) * c;
st[rt].lazy = c;
return;
}
pushdown(rt);
int mid = (st[rt].l + st[rt].r) >> 1;
if(mid >= L)
update(L, R, rt<<1, c);
if(mid < R)
update(L, R, rt<<1|1, c);
pushup(rt);
}
int findleft(int l)
{
int r = n;
while(l < r)
{
int mid = (r + l)>>1;
if(query(l, mid, 1) >= 1)
r = mid;
else
l = mid+1;
}
return r;
}
int findright(int t, int num)
{
int l = t;
int r = n;
while(l < r)
{
int mid = (l + r)>>1;
if(query(t, mid, 1) >= num)
r = mid;
else
l = mid+1;
}
return r;
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
scanf("%d %d", &n, &m);
buildtree(1, n, 1);
int op, a, b;
while(m--)
{
scanf("%d %d %d", &op, &a, &b);
if(op == 1)
{
a++;
int left = query(a, n, 1);
//printf("%d\n", left);
if(left == 0)
{
printf("Can not put any one.\n");
continue;
}
int l = findleft(a);
int r = findright(a, min(left, b));
printf("%d %d\n", l-1, r-1);
update(l, r, 1, 0);
}
else
{
a++;
b++;
printf("%d\n", (b-a+1) - query(a, b, 1));
update(a, b, 1, 1);
}
}
printf("\n");
}
return 0;
}