P5568 题解
题意简述
对一个空集
求最终的
题目分析
很直观的线段树题,分建树、修改、查询分别展开说明。
建树
由于涉及到的区间都是自然数区间,因此需要维护的区间个数有限,用线段树的结点维护就可以了。
为了方便维护,我们可以将所有自然数自身组成的闭区间 (这种区间按理说是不合法的,但是本题都这么写了就将就用吧) 编号为
我们考虑线段树结点维护的信息。最基本地,每个结点维护其对应区间的两端“单位区间”的编号
修改
分析一下本题的五种操作:
对于操作
对于操作
对于操作
对于操作
对于操作
查询
题目要求输出在
我们从根结点开始查询。当前结点满足 empty set
。
答案区间的合并是简单的,不再赘述。至于输出,根据区间的端点奇偶性判断区间开闭并计算原编号即可。
代码实现
#include<bits/stdc++.h> using namespace std; struct node { int l,r,cnt,tag; }tr[524300]; char op[2],str[50]; int l[70010],r[70010],i,q,OP[70010],mx=INT_MIN,mn=INT_MAX,ans1[140010],ans2[140010],cnt,L; bool pt(int l,int r) { printf("%c%d,%d%c ",(l&1?'(':'['),l>>1,r+1>>1,(r&1?')':']')); } void pushup(int p) { tr[p].cnt=tr[p<<1].cnt+tr[p<<1|1].cnt; } void addtag(int p,int val) { if(!val) { tr[p].tag=0; tr[p].cnt=0; }//如果是全改成 0,直接设置即可。 else if(val==1) { tr[p].tag=1; tr[p].cnt=tr[p].r-tr[p].l+1; }//全改成 1 也类似。 else//取反情况较复杂。 { tr[p].cnt=tr[p].r-tr[p].l+1-tr[p].cnt; if(tr[p].tag==2)//如果原来就要取反,再取反就不变了。 tr[p].tag=-1; else if(tr[p].tag>-1)//如果原来是要全改成什么,取反后就是改成相反的数了 。 tr[p].tag=(!tr[p].tag); else tr[p].tag=2;//如果原来不改,就记上标记。 } }//给结点 p 进行操作 val void pushdown(int p) { if(tr[p].tag!=-1) { addtag(p<<1,tr[p].tag);//传给左子结点 addtag(p<<1|1,tr[p].tag);//传给右子结点 tr[p].tag=-1;//清空标记 } } void build(int p,int l,int r) { tr[p].l=l,tr[p].r=r; tr[p].tag=-1;//初始没有标记。 if(l==r) { tr[p].cnt=0;//初始 S 为空 return; } int mid=l+r>>1; build(p<<1,l,mid);//建左子节点 build(p<<1|1,mid+1,r);//建右子节点 pushup(p); } void change(int p,int l,int r,int val) { if(tr[p].l>=l&&tr[p].r<=r) { addtag(p,val);//当前结点全在修改区间里,直接改。 return; } pushdown(p);//先传标记 int mid=tr[p].l+tr[p].r>>1; if(l<=mid)//左子结点要改 change(p<<1,l,r,val); if(r>mid)//右子结点要改 change(p<<1|1,l,r,val); pushup(p); } void query(int p) { pushdown(p);//先传标记。 if(tr[p].cnt==tr[p].r-tr[p].l+1) { ans1[++cnt]=tr[p].l; ans2[cnt]=tr[p].r;//把结点的左右编号放到答案里 return; } if(tr[p<<1].cnt) query(p<<1);//左子结点有在 S 里的就查询 if(tr[p<<1|1].cnt) query(p<<1|1);//右子结点有在 S 里的就查询 } int main() { while(scanf("%s %s",op+1,str+1)==2) { q++; l[q]=r[q]=0; for(i=2;str[i]<='9'&&str[i]>='0';i++) l[q]=l[q]*10+str[i]-'0'; for(i++;i<strlen(str+1);i++) r[q]=r[q]*10+str[i]-'0'; if(str[1]=='[') l[q]=2*l[q]; else l[q]=2*l[q]+1; if(str[strlen(str+1)]==']') r[q]=2*r[q]; else r[q]=2*r[q]-1; if(op[1]=='U') OP[q]=1; else if(op[1]=='I') OP[q]=2; else if(op[1]=='D') OP[q]=3; else if(op[1]=='C') OP[q]=4; else OP[q]=5; mx=max(mx,r[q]); mn=min(mn,l[q]); //省空间,只在修改涉及到的最小值到最大值之间建树即可 } mn--;//注意修改时会涉及到 mn-1 和 mx+1 mx++; build(1,mn,mx); for(int i=1;i<=q;i++) { if(OP[i]==1) change(1,l[i],r[i],1); else if(OP[i]==2) change(1,mn,l[i]-1,0),change(1,r[i]+1,mx,0); else if(OP[i]==3) change(1,l[i],r[i],0); else if(OP[i]==4) change(1,mn,l[i]-1,0),change(1,l[i],r[i],2),change(1,r[i]+1,mx,0); else change(1,l[i],r[i],2); } if(!tr[1].cnt)//S 为空 { printf("empty set"); return 0; } query(1);//查询 L=ans1[1]; for(int i=2;i<=cnt;i++) if(ans1[i]-ans2[i-1]!=1) { pt(L,ans2[i-1]); L=ans1[i]; } pt(L,ans2[cnt]);//输出 return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!