zoukankan      html  css  js  c++  java
  • 【线段树】USACO 08FEB Hotel G

    题目链接

    题目解析

    查询操作看起来有点麻烦,但完全可以用二分的思路。

    只需要记录下左右儿子有多少个连续的空房间就可以判断起点在左边还是右边了。

    当然,还要考虑区间合并,所以又是喜闻乐见的类似于维护最大子段和的方法。

    维护这样一些域值:

    (sum:)该区间内连续的最大空房间数

    (lsum:)包含左端点的连续的最大空房间数

    (rsum:)包含右端点的连续的最大空房间数

    (tag:)懒标记,表示这个区间全满/全空

    由于要维护懒标记,和维护爸爸的(sum),所以需要知道是否全空,也就是要维护一个(len)表示区间长度


    ►Code View

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<vector>
    #include<queue>
    using namespace std;
    #define LL long long
    #define N 50005
    #define DEL 100000
    #define INF 0x3f3f3f3f
    int rd()
    {
    	int x=0,f=1;char c=getchar();
    	while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
    	while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48); c=getchar();}
    	return f*x;
    }
    int n,m;
    struct node{
    	int sum,lsum,rsum,len;
    	int tag;//懒标记 tag==1 开房 tag==2退房 
    }tree[N<<2];
    void Build(int i,int l,int r)
    {
    	tree[i].sum=tree[i].lsum=tree[i].rsum=tree[i].len=r-l+1;
    	tree[i].tag=0;
    	if(l==r) return ;
    	int mid=(l+r)>>1;
    	Build(i<<1,l,mid);
    	Build(i<<1|1,mid+1,r);
    }
    void PushUp(int i)
    {
    	tree[i].sum=max(max(tree[i<<1].sum,tree[i<<1|1].sum),tree[i<<1].rsum+tree[i<<1|1].lsum);
    	if(tree[i<<1].sum==tree[i<<1].len)
    		tree[i].lsum=tree[i<<1].sum+tree[i<<1|1].lsum;
    	else tree[i].lsum=tree[i<<1].lsum;
    	if(tree[i<<1|1].sum==tree[i<<1|1].len)
    		tree[i].rsum=tree[i<<1|1].sum+tree[i<<1].rsum;
    	else tree[i].rsum=tree[i<<1|1].rsum;
    } 
    void PushDown(int i)
    {
    	if(tree[i].tag==0) return ;
    	tree[i<<1].tag=tree[i<<1|1].tag=tree[i].tag;
    	if(tree[i].tag==1)//开房
    	{
    		tree[i<<1].sum=tree[i<<1].lsum=tree[i<<1].rsum=0;
    		tree[i<<1|1].sum=tree[i<<1|1].lsum=tree[i<<1|1].rsum=0;
    	}
    	else
    	{
    		tree[i<<1].sum=tree[i<<1].lsum=tree[i<<1].rsum=tree[i<<1].len;
    		tree[i<<1|1].sum=tree[i<<1|1].lsum=tree[i<<1|1].rsum=tree[i<<1|1].len;
    	}
    	tree[i].tag=0;
    }
    int Query(int i,int l,int r,int len)
    {//这个查询有点像分治的样子(不过话说线段树本身就是分治的思想吧 
    	if(l==r) return l;//找到了 
    	PushDown(i); 
    	//不过正常情况下很少会在这里返回? 毕竟叶节点最多只有一个空房间 一般都是在下面返回吧
    	int mid=(l+r)>>1;
    	if(tree[i<<1].sum>=len) return Query(i<<1,l,mid,len);//左边房间够多
    	if(tree[i<<1].rsum+tree[i<<1|1].lsum>=len)//在中间能够找到足够的房间 
    		return mid-tree[i<<1].rsum+1;
    	return Query(i<<1|1,mid+1,r,len);
    }
    void Update(int i,int l,int r,int ql,int qr,int opt)
    {//opt==1 入住 opt==2 退房 
    	if(ql<=l&&r<=qr)
    	{
    		if(opt==1) tree[i].sum=tree[i].lsum=tree[i].rsum=0;
    		else tree[i].sum=tree[i].lsum=tree[i].rsum=tree[i].len;
    		tree[i].tag=opt;
    		return ;
    	}
    	PushDown(i);
    	int mid=(l+r)>>1;
    	if(ql<=mid) Update(i<<1,l,mid,ql,qr,opt);
    	if(qr>mid) Update(i<<1|1,mid+1,r,ql,qr,opt);
    	PushUp(i);
    }
    int main()
    {
    	n=rd(),m=rd();
    	Build(1,1,n);
    	while(m--)
    	{
    		int opt=rd();
    		if(opt==1)
    		{
    			int x=rd();
    			if(tree[1].sum<x)
    			{//没有连续x个空房间 
    				puts("0");
    				continue;
    			}
    			int l=Query(1,1,n,x);
    			printf("%d
    ",l);
    			Update(1,1,n,l,l+x-1,1);
    		}
    		else
    		{
    			int l=rd(),x=rd();
    			Update(1,1,n,l,l+x-1,2);
    		}
    	}
    	return 0;
    } 
    
  • 相关阅读:
    【转】SQL时间函数
    C#操作Word完全方法
    出水芙蓉,风华绝代记民国才女林徽因
    梅超风:我就是那个多年以前的女子
    厉胜男
    南海恶神
    挪窝
    吴若权——洛可可动画电影馆
    美的慢箭
    机械公敌(I, Robot) 场景设定
  • 原文地址:https://www.cnblogs.com/lyttt/p/13996639.html
Copyright © 2011-2022 走看看