Language:
区间
Time Limit: 6000MS | | Memory Limit: 131072K | Total Submissions: 8105 | | Accepted: 1892 | Case Time Limit: 2000MS |
Description
LogLoader是一家专门提供日志分析产品的公司。Ikki在做毕业设计的同时,还忙于在LogLoader做实习。在他的工作里,有一项是要写一个模块来处理时间区间。这个事情一直让他感到很迷糊,所以现在他很需要你帮忙。
在离散数学里面,你已经学习了几种基本的集合运算,具体地说就是并、交、相对补和对称差。它们自然地也适用于区间这种特殊的集合。作为你的快速参考,它们可以总结成下表:
运算 | 记号 | 定义 |
---|
并 | A ∪ B | {x : x ∈ A或x ∈ B} | 交 | A ∩ B | {x : x ∈ A并x ∈ B} | 相对补 | A − B | {x : x ∈ A但是x ∉B} | 对称差 | A ⊕ B | (A − B) ∪ (B − A) |
Ikki已经把他的工作里出现的区间运算抽象成一个很小的编程语言。他想你为他实现一个解析器。这个语言维护一个集合S。S一开始是空集,并根据下列命令被修改:
命令 | 语义 |
---|
U T | S ← S ∪ T | I T | S ← S ∩ T | D T | S ← S − T | C T | S ← T − S | S T | S ← S ⊕ T |
Input
输入包含一组测试数据,由0到65,535条命令组成。每条命令占一行,形式如下:
X T
其中X 是‘U ’、‘I ’、‘D ’、‘C ’和‘S ’中的一个,T是一个区间, 形式为( a, b) 、( a, b] 、[ a, b) 和[ a, b] 之一(a, b ∈ Z; 0 ≤ a ≤ b ≤ 65,535),取它们通常的意义。命令按在输入中出现的顺序执行。
文件结束符(EOF)表示输入结束。
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) Source
Translator
Yingchong SITU 'frkstyc'
这道题搞了整个下午+晚上,各种粗心大意wr到死了
终于搞定了,得管管自己的粗心了。。。。。
/*
采用线段树,对线段区间进行0,1标记表示该区间是否包含在s内
U T S ← S ∪ T 即将[l,r]标记为1
I T S ← S ∩ T 即将-oo~l和r~+oo标记为0,因为是并集,所以并集后的集合s一定在[l,r]内,则在l,r内的集合被标记是什么状态就是什么状态(表示是否属于s),[l,r]外的集合不属于s所以标记为0
D T S ← S - T 即将[l,r]标记为0,则在[l,r]内被s包含的集合也会标记为0表示不再属于s
C T S ← T - S 即先将-oo~l,r~+oo标记为0,这部分不属于[l,r]则一定不属于s,然后将[l,r]的标记0/1互换,因为属于s的不再属于s,不属于s的将属于s
S T S ← S ⊕ T 即属于s的不变,[l,r]中不属于s的(区间)0标记为1,属于s的(区间)1标记为0,所以[l,r]的标记0/1互换
最后对区间l,r标记时标记将l*2,r*2标记,如果是闭区间则对l*2+1,或r*2-1进行标记,则输出的时候只需判断奇偶就能判断开闭区间
是否覆盖0,1是否转换0,1的0,1转换都可以用异或去转换
*/
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<queue>
#include<algorithm>
#include<map>
#include<iomanip>
#define INF 99999999
using namespace std;
const int MAX=65535*2+2;
int cover[MAX<<2],Xor[MAX<<2];//cover表示区间的0,1状态,Xor表示该区间转换的状态
int mark[MAX];
void UpXor(int n){
if(cover[n] != -1)cover[n]^=1;//表示整个区间可以同时转换
else Xor[n]^=1;
}
void Upchild(int n){
if(cover[n] != -1){//表示该节点标记了但是孩子未标记
cover[n<<1]=cover[n<<1|1]=cover[n];
Xor[n<<1]=Xor[n<<1|1]=0;//区间全被覆盖则原来的转换无任何作用了
cover[n]=-1;
}
if(Xor[n]){//表示该点转换了但是孩子未转换
UpXor(n<<1);
UpXor(n<<1|1);
Xor[n]=0;
}
}
void Update(int L,int R,char ch,int n,int left,int right){
if(L<=left && right<=R){
if(ch == 'U')cover[n]=1,Xor[n]=0;
if(ch == 'D')cover[n]=Xor[n]=0;
if(ch == 'S' || ch == 'C')UpXor(n);
return;
}
Upchild(n);//是否该节点标记或转换过而孩子未标记或转换
int mid=left+right>>1;
if(L<=mid)Update(L,R,ch,n<<1,left,mid);
else if(ch == 'I' || ch == 'C')cover[n<<1]=Xor[n<<1]=0;//将-oo~l中的-oo~mid标记为0
if(R>mid)Update(L,R,ch,n<<1|1,mid+1,right);
else if(ch == 'I' || ch == 'C')cover[n<<1|1]=Xor[n<<1|1]=0;//将r~+oo中的mid+1~+oo标记为0
}
void Query(int n,int left,int right){
if(cover[n] == 1){
for(int i=left;i<=right;++i)mark[i]=true;
return;
}
if(cover[n] == 0)return;
if(left == right)return;
Upchild(n);
int mid=left+right>>1;
Query(n<<1,left,mid);
Query(n<<1|1,mid+1,right);
}
int main(){
int l,r;
char a,b,c;
while(scanf("%c %c%d,%d%c",&a,&b,&l,&r,&c)!=EOF){
l<<=1,r<<=1;
if(b == '(')++l;
if(c == ')')--r;
if(l>r){//比如输入区间为I (1,1)或C (1,1)
if(a == 'I' || a == 'C')cover[1]=Xor[1]=0;//将-oo~l,r~+oo标记为0
}else Update(l,r,a,1,0,MAX);
getchar();
}
bool flag=false;
int s=-1,t=-1;
Query(1,0,MAX);
for(int i=0;i<MAX;++i){
if(mark[i])s=(s==-1?i:s),t=i;
else if(s != -1){
if(flag)printf(" ");
flag=true;
printf("%c%d,%d%c",s&1?'(':'[',s>>1,(t+1)>>1,t&1?')':']');
s=-1;
}
}
if(!flag)printf("empty set");
printf("\n");
return 0;
}
|