题目链接:http://poj.org/problem?id=3225
思路:
我们一个一个操作来分析:(用0和1表示是否包含区间,-1表示该区间内既有包含又有不包含)
U:把区间[l,r]覆盖成1
I:把[-∞,l)(r,∞]覆盖成0
D:把区间[l,r]覆盖成0
C:把[-∞,l)(r,∞]覆盖成0 ,且[l,r]区间0/1互换
S:[l,r]区间0/1互换
成段覆盖的操作很简单,比较特殊的就是区间0/1互换这个操作,我们可以称之为异或操作
很明显我们可以知道这个性质:当一个区间被覆盖后,不管之前有没有异或标记都没有意义了
所以当一个节点得到覆盖标记时把异或标记清空
而当一个节点得到异或标记的时候,先判断覆盖标记,如果是0或1,直接改变一下覆盖标记,不然的话改变异或标记
开区间闭区间只要数字乘以2就可以处理(偶数表示端点,奇数表示两端点间的区间)
线段树功能:update:成段替换,区间异或 query:简单hash
说实话,自己现在单纯自己思考的话的确还是无法驾驭这种题的,用线段树操作的部分不算难。
主要点在怎么覆盖还有异或的处理上,尤其是异或的处理很让我蛋疼。
Lazy标记得用的炉火纯青,偶还未达到那个高度。
另外自己更加注意了一些细节性的东西,比如题目是否可能输入0。 涉及读入%c以及回车的时候要万分小心。
学到一些技巧,比如开闭区间的处理只要乘以2判断奇偶性即可。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
const int maxn=131072;
#define lson rt<<1,l,m
#define rson rt<<1|1,m+1,r
int Xor[maxn<<2];
int cover[maxn<<2];
int hash[maxn<<2];
void Fxor(int rt) //取异或@@
{
if(cover[rt]!=-1)
{
cover[rt]^=1;
}
else //cover[rt]==-1说明rt所关联的这块区域既有包含又有不包含 所以直接把这一块区间取异或是不对的
{
Xor[rt]^=1;
}
}
void Pushdown(int rt)
{
if(cover[rt]!=-1)
{
cover[rt<<1]=cover[rt<<1|1]=cover[rt];
Xor[rt<<1]=Xor[rt<<1|1]=0; //已覆盖,异或标记清零
cover[rt]=-1;
}
//这地方还得有一个判断异或标记。针对cover[rt]=-1的情况
if(Xor[rt])
{
Fxor(rt<<1);
Fxor(rt<<1|1);
Xor[rt]=0;
}
}
void update(int a,int b,char ch,int rt,int l,int r)//记好自己的习惯 l,r是指结点所表示的区间
{
if(a<=l&&b>=r)
{
if(ch=='U')
{
cover[rt]=1;
Xor[rt]=0;
}
else if(ch=='D')
{
cover[rt]=0;
Xor[rt]=0;
}
else if(ch=='S'||ch=='C') //这地方的技巧 (-8,a),(b,+8)的处理放在了后面
{
Fxor(rt);
}
return;
}
if(l==r) return;
Pushdown(rt);
int m=(l+r)>>1;
if(a<=m) //a,b与l,r混了。囧
{
update(a,b,ch,lson);
}
else
{
if(ch=='I'||ch=='C')
Xor[rt<<1]=cover[rt<<1]=0;
}
if(b>m)
{
update(a,b,ch,rson);
}
else
{
if(ch=='I'||ch=='C')
Xor[rt<<1|1]=cover[rt<<1|1]=0;
}
}
void query(int rt,int l,int r)
{
if(cover[rt]==1)
{
for(int i=l;i<=r;i++)
{
hash[i]=1;
}
return;
}
else if(cover[rt]==0) return;
if(l==r) return;
Pushdown(rt);
int m=(l+r)>>1;
query(lson);
query(rson);
}
int main()
{
// freopen("input.txt","r",stdin);
cover[1]=Xor[1]=0; //初始化
char op;
char c1,c2;
int a,b;
while(scanf("%c%*c%c%d,%d%c\n",&op,&c1,&a,&b,&c2)!=EOF)//这地方去掉回车就不对!!!
{
a*=2;b*=2;
if(c1=='(') a++;
if(c2==')') b--;
if(a>b) //(3,3)感觉这种情况不会出现,出现即全部清零,加上也没错
{
cover[1]=0;
Xor[1]=0;
}
else update(a,b,op,1,0,maxn);//操作数可能有0
}
query(1,0,maxn);
bool flag=false;
//for(int i=0;i<=10;i++) printf("%d\n",hash[i]);
int temp=1;
for(int i=0;i<=maxn;i++) //处理多个非连续区间的技巧
{
if(hash[i]==1)
{
flag=true;
if(temp==1)
{
if(i%2==1) printf("(%d,",i/2);
else printf("[%d,",i/2);
}
temp=2;
}
else if(hash[i]==0)
{
if(temp==2)
{
if((i-1)%2==1) printf("%d)",i/2);
else printf("%d]",i/2);
printf(" ");
}
temp=1;
}
}
if(!flag)
printf("empty set\n");
return 0;
}