zoukankan      html  css  js  c++  java
  • BZOJ2028:[SHOI2009]会场预约(线段树版)

    浅谈树状数组与线段树:https://www.cnblogs.com/AKMer/p/9946944.html

    题目传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=2028

    这题一开始我只会平衡树写法,打死都想不出线段树怎么写。然后偷偷去瞄一眼题解,里面说到了线段树染色。然后就一直往这个方向想。发现这就是区间赋值,对于第(i)个区间全部赋值成(i)就行了……然后对于每个区间有多少个不同的颜色段怎么更新又成了问题。我们记录一下每个区间最左边和最右边的颜色,合并的时候看看中间能不能拼上就行了。对于每次新的预约,与(l)(r)相交的区间直接强行擦去(用(0)号颜色覆盖),然后再全部覆盖新的颜色。

    时间复杂度:(O(nlog10^5))

    空间复杂度:(O(10^5))

    代码如下:

    #include <cstdio>
    using namespace std;
    
    const int maxn=2e5+5;
    
    int n;
    char s[10];
    int st[maxn],ed[maxn];
    
    int read() {
    	int x=0,f=1;char ch=getchar();
    	for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    	for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
    	return x*f;
    }
    
    struct segment_tree {
    	int num[maxn<<2],lft[maxn<<2],rgt[maxn<<2],tag[maxn<<2];//num[i]记录i号区间内不同颜色的段数,lft记录i号区间最左边的颜色,rgt记录最右边的,tag为颜色覆盖标记
    
    	void push_down(int p) {
    		if(tag[p]!=-1) {
    			tag[p<<1]=lft[p<<1]=rgt[p<<1]=tag[p];
    			tag[p<<1|1]=lft[p<<1|1]=rgt[p<<1|1]=tag[p];
    			num[p<<1]=num[p<<1|1]=(tag[p]!=0);
    			tag[p]=-1;//下传标记,覆盖颜色
    		}
    	}
    	
    	int query(int p,int l,int r,int pos) {
    		if(l==r)return lft[p];//递归到底了直接返回
    		if(lft[p]&&(lft[p]==rgt[p]))return lft[p];//如果整个区间都已经有颜色了就不需要递归了
    		int mid=(l+r)>>1;push_down(p);
    		if(pos<=mid)return query(p<<1,l,mid,pos);
    		return query(p<<1|1,mid+1,r,pos);
    	}
    
    	void updata(int p) {
    		num[p]=num[p<<1]+num[p<<1|1];//直接相加
    		if(rgt[p<<1]&&(rgt[p<<1]==lft[p<<1|1]))num[p]--;//如果中间的一样那就减一
    		lft[p]=lft[p<<1];rgt[p]=rgt[p<<1|1];//更新lft和rgt
    	}
    
    	void change(int p,int l,int r,int L,int R,int v) {
    		if(L<=l&&r<=R) {tag[p]=v;num[p]=(v!=0);lft[p]=rgt[p]=v;return;}//如过v不为0才算有颜色
    		int mid=(l+r)>>1;push_down(p);
    		if(L<=mid)change(p<<1,l,mid,L,R,v);
    		if(R>mid)change(p<<1|1,mid+1,r,L,R,v);
    		updata(p);
    	}
    }T;
    
    int main() {
    	n=read();
    	for(int i=1;i<=400000;i++)T.tag[i]=-1;//初始标记为-1,因为可能会有用0号颜色覆盖
    	for(int i=1;i<=n;i++) {
    		scanf("%s",s+1);
    		if(s[1]=='A') {
    			int l=read(),r=read();
    			st[i]=l;ed[i]=r;
    			int fake1=T.query(1,1,100000,l);
    			int fake2=T.query(1,1,100000,r);//fake1号预约与l相交,fake2与r相交
    			int tmp=T.num[1];
    			if(fake1)T.change(1,1,100000,st[fake1],ed[fake1],0);
    			if(fake2)T.change(1,1,100000,st[fake2],ed[fake2],0);//擦去
    			T.change(1,1,100000,l,r,i);
    			tmp=tmp-T.num[1]+1;printf("%d
    ",tmp);//之前有的预约减去现在的预约数减一就是拒绝的预约数
    		}
    		else printf("%d
    ",T.num[1]);//1号结点覆盖了全部区间
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    Windows Server 2008 定时任务卡住了不执行
    Oracle正则表达式取得所有非匹配的查询结果
    Websphere6.1.x不打印Log4j日志问题解决办法
    js的parseInt函数结果为0很奇怪的问题
    ORA12514: TNS:listener does not currently know of service问题原因
    Windows Server 2003升级IE6到高版本系统不支持解决方法
    Windows Server 2008 定时任务卡住了不执行
    Oracle正则表达式取得所有非匹配的查询结果
    Websphere6.1.x不打印Log4j日志问题解决办法
    Hibernate保存数据自动生成主键出现奇怪异常Duplicate entry '0' for key 1
  • 原文地址:https://www.cnblogs.com/AKMer/p/9948789.html
Copyright © 2011-2022 走看看