• 线段树 (区间合并)【p2894】[USACO08FEB]酒店Hotel


    Descripion

    奶牛们最近的旅游计划,是到苏必利尔湖畔,享受那里的湖光山色,以及明媚的阳光。作为整个旅游的策划者和负责人,贝茜选择在湖边的一家著名的旅馆住宿。这个巨大的旅馆一共有N (1 <= N <= 50,000)间客房,它们在同一层楼中顺次一字排开,在任何一个房间里,只需要拉开窗帘,就能见到波光粼粼的湖面。

    贝茜一行,以及其他慕名而来的旅游者,都是一批批地来到旅馆的服务台,希望能订到D_i (1 <= D_i <= N)间连续的房间。服务台的接待工作也很简单:如果存在r满足编号为r..r+D_i-1的房间均空着,他就将这一批顾客安排到这些房间入住;如果没有满足条件的r,他会道歉说没有足够的空房间,请顾客们另找一家宾馆。如果有多个满足条件的r,服务员会选择其中最小的一个。

    旅馆中的退房服务也是批量进行的。每一个退房请求由2个数字X_i、D_i描述,表示编号为X_i..X_i+D_i-1 (1 <= X_i <= N-D_i+1)房间中的客人全部离开。退房前,请求退掉的房间中的一些,甚至是所有,可能本来就无人入住。

    而你的工作,就是写一个程序,帮服务员为旅客安排房间。你的程序一共需要处理M (1 <= M < 50,000)个按输入次序到来的住店或退房的请求。第一个请求到来前,旅店中所有房间都是空闲的。

    表示这题很坑爹.

    但是的确是一道线段树好题.

    线段树需要维护的东西是这些

    struct cod{
    	int l;//左起最长连续空房长度
    	int r;//右起最长连续空房长度
    	int len;//区间长度.
    	int tg;//lazy标记
    	int sum;//区间最长连续空房长度
    }tr[N<<4];
    

    知道维护的东西了,现在我们就考虑如何更新,如何下放标记.

    PS:这里以(1)表示有人住房.(2)表示退房

    下放操作是这样的.//应该不难理解.

    inline void down(int o)
    {
    	if(!tr[o].tg)return;
    	tr[ls].tg=tr[rs].tg=tr[o].tg;//传递lazy标记
    	tr[ls].l=tr[ls].r=tr[ls].sum= (tr[o].tg==1 ? 0:tr[ls].len);//判断情况
    	tr[rs].l=tr[rs].r=tr[rs].sum= (tr[o].tg==1 ? 0:tr[rs].len);//判断情况
    	tr[o].tg=0;//清除lazy标记
    }
    

    然后考虑向上更新.

    我们需要维护(tr[o].l)(tr[o].r),还有(tr[o].sum)

    维护(tr[o].l),需要考虑左子区间是否全部是空房.()

    如果全部是,那么我们的

    (tr[o].l=tr[ls].sum+tr[rs].l)

    否则就是(tr[o].l=tr[ls].l)

    这就类似于区间合并.//不是很难理解吧.

    维护(tr[o].r)类似于维护(tr[o].l).

    维护(tr[o].sum)就有三种情况,

    1. 要么是(tr[ls].sum)
    2. 要么是(tr[rs].sum)
    3. 要么是(tr[ls].r+tr[rs].l)

    (max)即可

    inline void up(int o)
    {
    	if(tr[ls].sum==tr[ls].len)
    		tr[o].l=tr[ls].sum+tr[rs].l;
    	else 
    		tr[o].l=tr[ls].l;
    	if(tr[rs].sum==tr[rs].len)
    		tr[o].r=tr[rs].sum+tr[ls].r;
    	else 
    		tr[o].r=tr[rs].r;
    	tr[o].sum=max(max(tr[ls].sum,tr[rs].sum),tr[ls].r+tr[rs].l);
    }
    

    其他操作不是很难理解,就不过多解释了.

    代码

    #include<cstdio>
    #include<cctype>
    #include<iostream>
    #define ls o<<1
    #define rs o<<1|1
    #define N 50008
    #define R register
    using namespace std;
    inline void in(int &x)
    {
    	int f=1;x=0;char s=getchar();
    	while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
    	while(isdigit(s)){x=x*10+s-'0';s=getchar();}
    	x*=f;
    }
    struct cod{
    	int l;
    	int r;
    	int len;
    	int tg;
    	int sum;
    }tr[N<<4];
    void build(int o,int l,int r)
    {
    	tr[o].l=tr[o].r=tr[o].len=tr[o].sum=(r-l+1);
    	if(l==r)return;
    	int mid=(l+r)>>1;
    	build(ls,l,mid);
    	build(rs,mid+1,r);
    }
    inline void down(int o)
    {
    	if(!tr[o].tg)return;
    	tr[ls].tg=tr[rs].tg=tr[o].tg;
    	tr[ls].l=tr[ls].r=tr[ls].sum= (tr[o].tg==1 ? 0:tr[ls].len);
    	tr[rs].l=tr[rs].r=tr[rs].sum= (tr[o].tg==1 ? 0:tr[rs].len);
    	tr[o].tg=0;
    }
    inline void up(int o)
    {
    	if(tr[ls].sum==tr[ls].len)
    		tr[o].l=tr[ls].sum+tr[rs].l;
    	else 
    		tr[o].l=tr[ls].l;
    	if(tr[rs].sum==tr[rs].len)
    		tr[o].r=tr[rs].sum+tr[ls].r;
    	else 
    		tr[o].r=tr[rs].r;
    	tr[o].sum=max(max(tr[ls].sum,tr[rs].sum),tr[ls].r+tr[rs].l);
    }
    void change(int o,int l,int r,int x,int y,int del)
    {
    	down(o);
    	if(x<=l and y>=r)
    	{
    		tr[o].tg=del;
    		if(del==1) tr[o].l=tr[o].r=tr[o].sum=0;
    		else tr[o].l=tr[o].r=tr[o].sum=tr[o].len;
    		return;
    	}
    	int mid=(l+r)>>1;
    	if(x<=mid)change(ls,l,mid,x,y,del);
    	if(y>mid)change(rs,mid+1,r,x,y,del);
    	up(o);
    }
    int query(int o,int l,int r,int x)
    {
    	down(o);
    	if(l==r)return l;
    	int mid=(l+r)>>1;
    	if(tr[ls].sum>=x) return query(ls,l,mid,x);
    	if(tr[ls].r+tr[rs].l>=x) return mid-tr[ls].r+1;
    	return query(rs,mid+1,r,x);
    }
    int n,m;
    int main()
    {
    	in(n),in(m);build(1,1,n);
    	for(R int i=1,opt,x,y;i<=m;i++)
    	{
    		in(opt);
    		if(opt==1)
    		{
    			in(x);
    			if(tr[1].sum<x)
    			{
    				puts("0");
    				continue;
    			}
    			int pos=query(1,1,n,x);
    			printf("%d
    ",pos);
    			change(1,1,n,pos,pos+x-1,1);
    		}
    		else 
    		{
    			in(x),in(y);
    			change(1,1,n,x,x+y-1,2);
    		}
    	}
    }
    
  • 相关阅读:
    LCS LIS
    补个线段树
    洛谷1522
    AC自动机
    WF 2017 I
    WF2017 E
    最小生成树计数 基尔霍夫矩阵树定理
    bitonic tour luogu1523
    code+11月月赛
    模拟退火
  • 原文地址:https://www.cnblogs.com/-guz/p/9846078.html
Copyright © 2020-2023  润新知