Vases and Flowers HDU - 4614【线段树+主席树思维/二分】
题意:
给出 \(n\) 个花瓶,编号 \([0,n-1]\),一开始每个花瓶是空的。输入 \(K\)。\(Alice\) 有两种操作,共 \(m\) 个:
\(K=1\):输入 \(A,F\)。\(Alice\) 将从花瓶 \(A\) 开始,向空花瓶中插入花,如果没有插完,则多余的花舍弃;
\(K=2\):输入 \(A,B\)。\(Alice\) 建将把花瓶 \(A,B\) 之间的花清除掉,求出清除的花的数量,\(A\leq B\);
数据范围: \(1 < N,M < 50001\)
分析1:
主席树求区间第 \(k\) 大的思想。以 \(1\) 表示花瓶为空,\(0\) 表示花瓶有花,把区间变成 \([1,n]\),输入位置相应 \(+1\)。当进行插花操作时,我们先求出 \([A,N]\) 区间内的空花瓶数目,假设为 \(t\)。如果 \(t=0\),表示无法插入花;否则,我们寻找到进行插花的花瓶的左右区间端点,然后利用简单的区间修改即可完成插花操作。如何找到端点呢?我们先求出区间 \([1,A-1]\) 内的空花瓶数,假设为 \(p\),那么只要找到整个区间的第 \(p+1\) 和第 \(p+min(F,t)\) 个空花瓶即可。这些操作,可以类比于主席树区间求第 \(k\) 大。
代码实现:
注意 \(lazy\) 标记,不能累加,直接修改即可。
区间修改时,如果区间满足 \(L\leq l\;\&\&\;r \leq R\) ,可以直接赋值。不用考虑区间内是否是空花瓶和有花花瓶共存的情况,\(lazy\) 标记下放时,也是如此。一开始把把这些区间区分开,写起来很麻烦,也任意错。具体见代码。
代码1:
#include <bits/stdc++.h>
using namespace std;
const int N=5e4+5;
int tree[N<<2],lazy[N<<2];
void build(int l,int r,int rt)
{
lazy[rt]=-1;
if(l==r)
{
tree[rt]=1;
return;
}
int mid=(l+r)>>1;
build(l,mid,rt<<1);
build(mid+1,r,rt<<1|1);
tree[rt]=tree[rt<<1]+tree[rt<<1|1];
}
void pushdown(int ln,int rn,int rt)
{
if(lazy[rt]!=-1)
{
lazy[rt<<1]=lazy[rt];
lazy[rt<<1|1]=lazy[rt];
if(lazy[rt]==1)
{
tree[rt<<1]=ln;
tree[rt<<1|1]=rn;
}
else
{
tree[rt<<1]=0;
tree[rt<<1|1]=0;
}
lazy[rt]=-1;
}
}
void update(int l,int r,int L,int R,int val,int rt)
{
if(L<=l&&r<=R)//注意
{
tree[rt]=val*(r-l+1);
lazy[rt]=val;
return;
}
int mid=(l+r)>>1;
pushdown(mid-l+1,r-mid,rt);
if(L<=mid)
update(l,mid,L,R,val,rt<<1);
if(R>mid)
update(mid+1,r,L,R,val,rt<<1|1);
tree[rt]=tree[rt<<1]+tree[rt<<1|1];
}
int query1(int l,int r,int L,int R,int rt)//简单区间求和
{
if(L<=l&&r<=R)
return tree[rt];
int mid=(l+r)>>1,ans=0;
pushdown(mid-l+1,r-mid,rt);
if(L<=mid)
ans+=query1(l,mid,L,R,rt<<1);
if(R>mid)
ans+=query1(mid+1,r,L,R,rt<<1|1);
return ans;
}
int query2(int l,int r,int L,int R,int k,int rt)//求区间第k个空花瓶
{
if(l==r)
return l;
int mid=(l+r)>>1;
pushdown(mid-l+1,r-mid,rt);
if(tree[rt<<1]>=k)
return query2(l,mid,L,R,k,rt<<1);
else
return query2(mid+1,r,L,R,k-tree[rt<<1],rt<<1|1);
}
int main()
{
int t,n,m,a,b,c,cot=0;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
build(1,n,1);
while(m--)
{
scanf("%d%d%d",&a,&b,&c);
if(a==1)
{
int t=query1(1,n,b+1,n,1);//区间求和,看是否有空花瓶
if(t==0)
printf("Can not put any one.\n");
else
{
int p=(b>0?query1(1,n,1,b,1):0);//[1,b]内的花瓶数量
int star=query2(1,n,1,n,p+1,1);
int en=query2(1,n,1,n,p+min(t,c),1);
update(1,n,star,en,0,1);//插花->0
printf("%d %d\n",star-1,en-1);
}
}
else
{
printf("%d\n",c-b+1-query1(1,n,b+1,c+1,1));
update(1,n,b+1,c+1,1,1);//清空花瓶->1
}
}
printf("\n");
}
return 0;
}