题目
题解
参考了黄学长的blog
想到用暴力做标记,发现了区间的操作,并且把区间左右端点的开与闭进行了保存,但是却没有想到线段树也没有上升为拆点的思想。
这里,我们把(1,2)区间拆成三个点——左端点,右端点和区间,如果是[1,2]的话就相当于拆成了一个点。看下图的拆点过程。
首先把每个点“分散”一下,然后把线段向右平移两个单位(为了为0留出位置),原来的每个点就代表的一个闭区间。
然后就进行区间的操作。
- U 区间标记1
- I 两边区间标记0
- D 区间标记1
- C 区间两边标记0,中间取反
- S 区间取反
然后时候线段树操作
输出看代码吧
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<iostream>
const int inf=1e9;
const int n=65535<<2|1;
int x,y;
struct Tree{
int l,r,tag,rev,col;//tag标记颜色 rev是否进行反转
}tree[n<<3];
int read()//拆点
{
char ch=getchar();int x=0,f=0;
while (ch<'0'||ch>'9') {if (ch=='(') f=1; ch=getchar();}
while (ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+ch-'0'; ch=getchar();
}
if (ch==')') f=-1;
x<<=1; x+=f+2;
return x;//x<<1+f+2 位运算的优先级比任何都低
}
void build_tree(int l,int r,int rt)
{
tree[rt].l=l; tree[rt].r=r; tree[rt].tag=-1;
if (l==r) return;
int mid=(l+r)>>1;
build_tree(l,mid,rt<<1);
build_tree(mid+1,r,rt<<1|1);
}
void pushdown(int rt)
{
int tag=tree[rt].tag,rev=tree[rt].rev;
tree[rt].tag=-1; tree[rt].rev=0;
if (tree[rt].l==tree[rt].r)
{
if (tag!=-1) tree[rt].col=tag;
tree[rt].col^=rev;//tag的优先级大于rev
return;
}
if (tag!=-1)
{
tree[rt<<1].tag=tree[rt<<1|1].tag=tag;
tree[rt<<1].rev=tree[rt<<1|1].rev=0;
}
tree[rt<<1].rev^=rev; tree[rt<<1|1].rev^=rev;
}
void change(int rt,int x,int y,int C)
{
if (x>y) return;
int l=tree[rt].l,r=tree[rt].r,mid=(l+r)>>1;
pushdown(rt);
if (l==x && r==y)
{
if (C==-1) tree[rt].rev^=1;
else tree[rt].tag=C;
return;
}
if (y<=mid) change(rt<<1,x,y,C);//重新选择区间
else if (x>mid) change(rt<<1|1,x,y,C);
else//分治
{
change(rt<<1,x,mid,C);
change(rt<<1|1,mid+1,y,C);
}
}
int ques(int rt,int x)
{
pushdown(rt);//询问前下传标记
int l=tree[rt].l,r=tree[rt].r;
if (l==r) return tree[rt].col;
int mid=(l+r)>>1;
if (x<=mid) return ques(rt<<1,x);
else return ques(rt<<1|1,x);
}
int main()
{
// freopen("interval.in","r",stdin);
// freopen("interval.out","w",stdout);
char opt[5];
build_tree(1,n,1);
while (scanf("%s",opt)!=EOF)
{
x=read(); y=read();
if (opt[0]=='U') change(1,x,y,1);
else if (opt[0]=='I') {change(1,1,x-1,0); change(1,y+1,n,0);}
else if (opt[0]=='D') change(1,x,y,0);
else if (opt[0]=='C') {change(1,1,x-1,0); change(1,y+1,n,0); change(1,x,y,-1);}
else if (opt[0]=='S') change(1,x,y,-1);//S区间取反
}
//shuchu
int start=-1,last=-1,flag=0;
for(int i=1;i<=n;i++)
if(ques(1,i))
{
if(start==-1)start=i;
last=i;
}
else
{
if(start!=-1)
{
if(flag)printf(" ");
else flag=1;
if(start&1)printf("(");
else printf("[");
printf("%d",start/2-1);
printf(",");
printf("%d",(last+1)/2-1);
if(last&1)printf(")");
else printf("]");
}
last=start=-1;
}
if(!flag)puts("empty set");
return 0;
int l=-1,r=-1; bool ifl=0,fir=0;//是否已经找到
for (int i=1; i<=n; i++)
{
if (ques(1,i))
if (!ifl) {l=i; ifl=1;}
else if (ifl) {r=i; break;}
}
if (!ifl) {printf("empty set"); return 0;}
char ansl,ansr;
ansl=(l&1)?'(':'['; ansr=(r&1)?')':']';
l=(l&1)?(l-3)>>1:(l-2)>>1; r=(r&1)?(r-1)>>1:(r-2)>>1;
printf("%c%d,%d%c ",ansl,l,r,ansr);
return 0;
}
/*
U [1,5]
D [3,3]
S [2,4]
C (1,5)
I (2,3]
(2,3) 每个区间后面带一个空格
*/
总结
好好学习线段树