您的位置:首页 > 其它

【bzoj3226】[Sdoi2008]校门外的区间

2016-07-06 09:46 232 查看
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;

4000
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;//不能打回车
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  线段树