题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4614
题意:给你N个花瓶编号为0~N-1,和M个操作,操作分为两种
1.将B支花一次插入从A之后(包括A)的花瓶中,如果花瓶为非空就插入到其之后的花瓶中,每个花瓶限制只能插一枝花。输出第一次花的花瓶的位置和最后一次插花的花瓶的位置(如果一枝花也不能插输出~~~~)
2. 将A~B这个区间上的所有花取出,输出取出花的个数。
思路:线段树~ 线段树只维护一个域就是此区间上是否插了花,如果插的mark为0,没查mark为1,-1表示没有标记。
下面是这道题目的关键,就是假设已经找到了插花的起点和终点,怎么快速的更新这个区间呢??? 方法是区间赋值,直降将整个区间全部都插上花,这样的话就能用打标记做了。
将区间中的花拔出来的方法同上。
如何快速查找到插花的位置呢? 先判断A点是否是插过花了,如果插过了就去找在A点右边且离A点最近的位置,记f(n)表示前n项的和,则f(s)=f(A)+1,s就是起点,终点和起点间有(B-1)个1,同样可以用前n项和的方法找到。
因为线段树维护的此区间是否全部为1或者为0,所以求和的方法跟维护和的时候是不太一样的,方法就是不断地下放当前位置的标记然后求和。
我的程序跑的1700ms+,看到最快的100ms+就能过,我简直是太弱了,不过是1A也很开心。
code:
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
typedef long long LL;
const int maxn=50021;
struct segtree
{
int l,r,mark;
}ss[4*maxn+10];
int n;
void Build(int k,int ll,int rr)
{
ss[k].l=ll;
ss[k].r=rr;
ss[k].mark=-1;
if(ll==rr) return ;
Build(2*k+1,ll,(ll+rr)/2);
Build(2*k+2,(ll+rr)/2+1,rr);
}
void init(int n_)
{
n=1;
while(n<n_)n*=2;
Build(0,0,n-1);
}
void Push_down(int k)
{
ss[2*k+1].mark=ss[k].mark;
ss[2*k+2].mark=ss[k].mark;
ss[k].mark=-1;
}
void update(int a,int b,int k,int ff)
{
if(b<ss[k].l||a>ss[k].r) return ;
if(a<=ss[k].l&&ss[k].r<=b) ss[k].mark=ff;
else{
if(ss[k].mark!=-1) Push_down(k);
update(a,b,2*k+1,ff);
update(a,b,2*k+2,ff);
}
}
int getsum(int a,int b,int k)
{
if(ss[k].l>b||ss[k].r<a) return 0;
if(a<=ss[k].l&&ss[k].r<=b&&ss[k].mark!=-1) return (ss[k].r-ss[k].l+1)*ss[k].mark;
else{
if(ss[k].mark!=-1) Push_down(k);
int v1=getsum(a,b,2*k+1);
int v2=getsum(a,b,2*k+2);
return v1+v2;
}
}
int query(int sum,int k)
{
if(ss[k].l==ss[k].r){
if(sum==ss[k].mark) return ss[k].l;
else return 0;
}
else{
if(ss[k].mark!=-1) Push_down(k);
int mid=getsum(ss[2*k+1].l,ss[2*k+1].r,0);
if(sum>mid) query(sum-mid,2*k+2);
else query(sum,2*k+1);
}
}
int main()
{
int T,N,M;
int K,A,B,s,t,sum0,sum1,sum2;
scanf("%d",&T);
while(T--){
scanf("%d%d",&N,&M);
init(N);
update(0,N-1,0,1);
update(N,n-1,0,0);
while(M--){
scanf("%d%d%d",&K,&A,&B);
if(K==1){
sum0=getsum(0,n-1,0);
sum1=getsum(0,A,0);
if(A==0) sum2=0;
else sum2=getsum(0,A-1,0);
if(sum0-sum2==0) printf("Can not put any one.\n");
else{
if(sum1>sum2){
s=A;
if(sum0-sum2<B) t=query(sum0,0);
else t=query(sum1+B-1,0);
}
else{
s=query(sum1+1,0);
if(sum0-sum2<B) t=query(sum0,0);
else t=query(sum1+B,0);
}
update(s,t,0,0);
printf("%d %d\n",s,t);
}
}
else{
if(A==0) printf("%d\n",B+1-getsum(0,B,0));
else printf("%d\n",(B-A+1)-(getsum(0,B,0)-getsum(0,A-1,0)));
update(A,B,0,1);
}
}
printf("\n");
}
return 0;
}