Bzoj 3226: [Sdoi2008]校门外的区间

以下来自 ShallWe's Blog

3226: [Sdoi2008]校门外的区间

Description

  受校门外的树这道经典问题的启发,A君根据基本的离散数学的知识,抽象出5种运算维护集合\(S\)(\(S\)初始为空)并最终输出S。现在,请你完成这道校门外的树之难度增强版——校门外的区间。
  基本集合运算如下:
\(U:A∪B=\{x:x∈A|x∈B\}\)
\(I:A∩B=\{x:x∈A \& x∈B\}\)
\(D:A-B=\{x:x∈A \& x∉B\}\)
\(C:B-A\)
\(S:A⊕B=(A-B)∪(B-A)\)

Input

  输入共M行。
  每行的格式为X T,用一个空格隔开,X表示运算的种类,T为一个区间(区间用\((a,b)\),\((a,b]\),\([a,b)\),\([a,b]\)表示)。

Output

  共一行,即集合\(S\),每个区间后面带一个空格。若S为空则输出"empty set"。
对于\(100%\)的数据,\(0≤a≤b≤65535\),\(1≤M≤70000\)

解题报告

应该说是比较容易(by dada见友联)的一个题,首先,按照高中数学必修1所学的Vane图以及集合的基本运算,可以得到每一种对于区间进行的集合操作的效果:

  1. U: B区间->1
  2. I: 非B区间->0
  3. D:B区间->0
  4. C: 取反、转2
  5. S: 区间取反

请花vane图加深感受.
然后区间取反区间覆盖,当然可以使用线段树、平衡树(?),但是这两种标记在线段树中是不可合并的,但其实无所谓因为在询问的时候没有区间查询,而一旦出现要标记合并的地方,就提前下传,就像iwtwiioi说过“不能合并就传下去”,然后就没什么细节了,输入输出的过程中,将\(()\)作为\(*2+/-1\),\([]\)作为\(*2\)

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
using namespace std;
const int N=131075;
int mark[N<<2],white[N<<2],rev[N<<2],m,n=131071;
char ch[10];
bool flag=0;
inline void in(int &x,int &y){
	char ch=getchar(); int tmp;
	for (;ch<'0'||ch>'9';ch=getchar()){
		if (ch=='(') x=1; 
		if (ch=='[') x=0; 
	}
	for (tmp=0;ch>='0'&&ch<='9';ch=getchar())
		tmp=tmp*10+ch-48;
	x+=tmp<<1;
	for (;ch<'0'||ch>'9';ch=getchar());
	for (tmp=0;ch>='0'&&ch<='9';ch=getchar())
		tmp=tmp*10+ch-48; 
	for (;ch!=']'&&ch!=')';ch=getchar()); 
	if (ch==']') y=0; 
	else y=-1; 
	y+=tmp<<1;
}
void build(int x,int l,int r){
	mark[x]=-1,white[x]=0; 
	if (l==r) return; 
	int mid=(l+r)>>1; 
	build(x<<1,l,mid); 
	build(x<<1|1,mid+1,r); 
}
inline void down(int x,int l,int r){
	int m=mark[x],re=rev[x]; 
	mark[x]=-1,rev[x]=0;
	if (l==r){
		if (m!=-1)
			white[x]=m;
		white[x]^=re;
		return;
	}
	if (m!=-1){
		mark[x<<1]=m;
		mark[x<<1|1]=m;
		rev[x<<1|1]=rev[x<<1]=0; 
	}
	rev[x<<1]^=re,rev[x<<1|1]^=re;
}
		
void add(int x,int l,int r,int L,int R,int val){
	down(x,l,r);	
	if (L<=l&&r<=R){
		if (val<2)
			mark[x]=val; 
		else
			rev[x]^=1; 
		return;
	}
	int mid=(l+r)>>1; 
	if (L<=mid) add(x<<1,l,mid,L,R,val); 
	if (R>mid) add(x<<1|1,mid+1,r,L,R,val); 
}
int query(int x,int l,int r,int pur){
	down(x,l,r);
	if (l==r) return white[x]; 
	int mid=(l+r)>>1; 
	if (pur<=mid) 
		return query(x<<1,l,mid,pur);
	else 
		return query(x<<1|1,mid+1,r,pur);
}
inline void print(int x,int y){
	if (x&1) printf("(%d,",x>>1); 
	else printf("[%d,",x>>1); 
	if (y&1) printf("%d)",y+1>>1); 
	else printf("%d]",y>>1);
	flag=1;
	printf(" ");
}
int main(){
//	freopen("interval.in","r",stdin); 
//	freopen("interval.out","w",stdout);
	build(1,0,n);
	int a,b;
	while (scanf("%s",ch)!=EOF){
		in(a,b);
        if (ch[0]=='U') 
			add(1,0,n,a,b,1);
        if (ch[0]=='I'){
        	if(a)
				add(1,0,n,0,a-1,0);
			add(1,0,n,b+1,n,0);
		}
        if (ch[0]=='D') 
			add(1,0,n,a,b,0);
        if (ch[0]=='C'){ 
			add(1,0,n,1,a-1,0);
			add(1,0,n,a,b,2);
			add(1,0,n,b+1,n,0);
		}
        if (ch[0]=='S') 
			add(1,0,n,a,b,2);
	}
	int s=-1,t=0;
	for (t=0;t<=n;t++)
		if (!query(1,0,n,t)){
			if (s!=-1) print(s,t-1),s=-1; 
		}else
			if (s==-1) s=t;
	if (s!=-1) print(s,n),s=-1; 
	if (!flag) printf("empty set");
	return 0; 
}
posted @ 2016-08-15 17:01  ShallWe2000  阅读(468)  评论(1编辑  收藏  举报
Return Top
Dreams writes for dreamers like me.