以下来自 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图以及集合的基本运算,可以得到每一种对于区间进行的集合操作的效果:
- U: B区间->1
- I: 非B区间->0
- D:B区间->0
- C: 取反、转2
- 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;
}