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\n",tmp);//之前有的预约减去现在的预约数减一就是拒绝的预约数
		}
		else printf("%d\n",T.num[1]);//1号结点覆盖了全部区间
	}
	return 0;
}

posted @ 2018-11-12 20:18  AKMer  阅读(225)  评论(0编辑  收藏  举报