Description
受校门外的树这道经典问题的启发,A君根据基本的离散数学的知识,抽象出5种运算维护集合S(S初始为空)并最终输出S。现在,请你完成这道校门外的树之难度增强版——校门外的区间。
5种运算如下:
U T S∪T
I T S∩T
D T S-T
C T T-S
S T S⊕T
基本集合运算如下:
A∪B:{x : xÎA or xÎB}
A∩B:{x : xÎA and xÎB}
A-B:{x : xÎA and xÏB}
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”。
Sample Input
U [1,5]
D [3,3]
S [2,4]
C (1,5)
I (2,3]
Sample Output
(2,3)
HINT
对于 100% 的数据,0≤a≤b≤65535,1≤M≤70000
Source
线段树
解题报告:
开闭区间取决于符号
那么我可以把(2,3]变成[2.5,3]
最后处理2.5就行了
为了方便,我们把整体乘二,最后讨论奇偶性来输出
并集是添加元素,输入[a,b]把a~b的元素赋值为1
交集是只能保留与输入范围重复的集合
输入范围重复代表输入范围以外全都要删去
则输入[a,b]就把1~a-1,b+1~65536全赋值为0
相减:A-B。则B中有的元素A中不能有
读入[a,b]则把a~b的元素全赋值为0
B-A。则A中有的元素B中不能有
则输入[a,b],把1~a-1,b+1~65536全赋值为0
再把a~b的元素取反
(A-B)∪(B-A)。
A-B是将1~a-1,b+1~65536的元素保留,B-A是将1~a-1,b+1~65536的元素清除,然后a~b的元素取反。
结合起来就是将a~b的元素取反。
如何用线段树实现
我们可在叶子节点存储是否有该元素,然后用col[]数组表示是否清零或添加
用rev[]数组表示是否取反
只在子节点存储0 1情况
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#define M 65600
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define n (M*2) //开成两倍大小
using namespace std;
int col[n<<2],rev[n<<2],sum[n<<2];//范围也要改变
int read()
{
int x=0,f=0;char ch;
ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='(')f=-1;
ch=getchar();}
while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
if(ch==')')f=1;
return x*2-f;
}
void build(int l,int r,int rt)
{
col[rt]=-1;
rev[rt]=0;
if(l==r){return;}
int mid=(l+r)>>1;
build(lson);
build(rson);
}
void pushdown(int rt,bool fl,int l)
{
if(fl)
{
if(col[rt]!=-1)sum[rt]=col[rt];
sum[rt]^=rev[rt];
rev[rt]=0;col[rt]=-1;
return;//return一定要有
}
if(col[rt]!=-1)
{
col[rt<<1]=col[rt];col[rt<<1|1]=col[rt];
rev[rt<<1]=0;rev[rt<<1|1]=0;
}
rev[rt<<1]^=rev[rt];
rev[rt<<1|1]^=rev[rt];
rev[rt]=0;col[rt]=-1;
}
void update(int L,int R,int c,int l,int r,int rt)
{
if(R<L)return;
pushdown(rt,(l==r),l);
if(L<=l && R>=r)
{
if(c==-1)rev[rt]^=1;
else col[rt]=c;
return;
}
int mid=(l+r)>>1;
if(L<=mid)update(L,R,c,lson);
if(R>mid)update(L,R,c,rson);
}
int query(int c,int l,int r,int rt)
{
pushdown(rt,(l==r),l);
if(l==r)return sum[rt];
int mid=(l+r)>>1;
if(c<=mid)return query(c,lson);
else return query(c,rson);
}
int main()
{
memset(col,-1,sizeof(col));
memset(sum,0,sizeof(sum));
memset(rev,0,sizeof(rev));
char ch1[50];
while(scanf("%s",ch1)!=EOF)
{
int a,b;
a=read();b=read();
a+=2;b+=2;
//cout<<ch1[0]<<" "<<a<<" "<<b<<endl;
switch(ch1[0])
{
case 'U':update(a,b,1,1,n,1);break;
case 'I':update(1,a-1,0,1,n,1);update(b+1,n,0,1,n,1);break;//case一定要有break!!!
case 'D':update(a,b,0,1,n,1);break;
case 'C':update(1,a-1,0,1,n,1);update(b+1,n,0,1,n,1);update(a,b,-1,1,n,1);break;
case 'S':update(a,b,-1,1,n,1);break;
}
}
int l=-1,r=-1,flag=0;
for(int i=1;i<=n;i++)
{
if(query(i,1,n,1))
{
if(l==-1)l=i;
r=i;
}
else
{
if(l!=-1)
{
if(flag==1)printf(" ");
else flag=1;
if(l&1)printf("(");
else printf("[");
printf("%d",l/2-1);
printf(",%d",(r+1)/2-1);//通过数学方法可知
if(r&1)printf(")");
else printf("]");
l=-1;r=-1;
}
}
}
if(flag==0)printf("empty set");
return 0;//不能打回车
}