问题 A: 联
时间限制: 2 Sec 内存限制: 256 MB
题面
题面谢绝公开。
题解
解法1:离散化+线段树。
1e18的数据范围直接离散化掉所有的l和r,加一个映射数组表示间距即可。
线段树维护区间和,扫0的时候判定子树和等不等于子树大小。
维护两个标记:lazy(将一个区间赋值成什么)、xr(是否异或整个区间)跑就完了。
(我会说我沉迷解法二而解法一是水的么 感谢Larry提供的优质讲解及代码)
#include#define int long long #define rint register int using namespace std; int m,a[200005<<1],tot=0,cnt; struct Tree{int l,r,sum,laz,flag;}t[200005<<3]; struct node{int l,r,val;}qu[200005]; inline void pushup(int k){t[k].sum=t[k<<1].sum+t[k<<1|1].sum;} inline void build(int k,int l,int r) { t[k].l=l,t[k].r=r, t[k].laz=-1,t[k].sum=0; if(l==r) return ; int mid=(l+r)>>1; build(k<<1,l,mid),build(k<<1|1,mid+1,r); } inline void down(int k) { int linl=t[k].l,linr=t[k].r,mid=(linl+linr)>>1; if(t[k].laz!=-1) { t[k<<1].laz=t[k<<1|1].laz=t[k].laz; t[k<<1].sum=t[k].laz*(mid-linl+1),t[k<<1|1].sum=t[k].laz*(linr-mid); t[k<<1].flag=t[k<<1|1].flag=0;t[k].laz=-1; } if(t[k].flag) { t[k<<1].flag^=1;t[k<<1|1].flag^=1;t[k].flag=0; t[k<<1].sum=(mid-linl+1)-t[k<<1].sum;t[k<<1|1].sum=(linr-mid)-t[k<<1|1].sum; } return ; } inline void change(int k,int l,int r,int val) { int linl=t[k].l,linr=t[k].r; if(l<=linl&&linr<=r) { t[k].sum=val*(linr-linl+1); t[k].laz=val;t[k].flag=0; return ; } down(k); int mid=(linl+linr)>>1; if(l<=mid) change(k<<1,l,r,val); if(r>mid) change(k<<1|1,l,r,val); pushup(k); } inline void Xor(int k,int l,int r) { int linl=t[k].l,linr=t[k].r; if(l<=linl&&linr<=r) { t[k].flag^=1; t[k].sum=(linr-linl+1)-t[k].sum; return ; } down(k); int mid=(linl+linr)>>1; if(l<=mid) Xor(k<<1,l,r); if(r>mid) Xor(k<<1|1,l,r); pushup(k); } inline int query(int k) { int linl=t[k].l,linr=t[k].r; if(linl==linr) return linl; down(k); int mid=(linl+linr)>>1; if(t[k<<1].sum<(mid-linl+1)) return query(k<<1); else return query(k<<1|1); } signed main() { scanf("%lld",&m); a[++tot]=1; for(rint i=1;i<=m;++i) { scanf("%lld %lld %lld",&qu[i].val,&qu[i].l,&qu[i].r); a[++tot]=qu[i].l,a[++tot]=qu[i].r+1; } sort(a+1,a+tot+1); cnt=unique(a+1,a+tot+1)-a-1; build(1,1,cnt); for(rint i=1;i<=m;++i) { qu[i].l=lower_bound(a+1,a+cnt+1,qu[i].l)-a; qu[i].r=lower_bound(a+1,a+cnt+1,qu[i].r+1)-a-1; if(qu[i].val==1) change(1,qu[i].l,qu[i].r,1); else if(qu[i].val==2) change(1,qu[i].l,qu[i].r,0); else Xor(1,qu[i].l,qu[i].r); printf("%lld\n",a[query(1)]); } return 0; }
解法2:这怕是个ODT裸题(emm可能有人不知道ODT这个高端大气的名字,那珂朵莉树应该无人不晓了吧2333)
代码里面有注释。(一会儿去补一篇珂朵莉树学习笔记)
(还是要%%%羊驼神赛时打珂朵莉树。面对满屏幕的编(炸)译(库)信息我真的要崩溃了的说。)
#include#define read(A) A=init() #define rint register int #define ll long long #define inf 1000000000000000000 using namespace std; inline int init() { int a=0,b=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')b=-1;ch=getchar();} while(ch>='0'&&ch<='9'){a=(a<<3)+(a<<1)+ch-'0';ch=getchar();} return a*b; } int m; struct node{ ll l,r;mutable int val; friend bool operator < (const node &A,const node &B){ return A.l<B.l; } }; set s; inline set ::iterator split(ll k) { set ::iterator it=s.lower_bound((node){k,0,-1}); if(it->l==k)return it;//若搜索的l是刚好一个区间的l,则会得到这个区间的位置 it--;//否则会得到靠后的一个区间,此时--得到之前的区间 ll linl=it->l,linr=it->r,linv=it->val;//取出当前区间的l、r、val s.erase(it);//暴力删除当前区间 s.insert((node){linl,k-1,linv});//将区间切割,将l-1与前区间的l形成新区间插回 return s.insert((node){k,linr,linv}).first;//返回值为pair,第一维为地址 } inline void change(ll l,ll r,int val) { set ::iterator itr=split(r+1),itl=split(l);//先通过切割取出集合左右端点 s.erase(itl,itr);//直接删除一个集合 s.insert((node){l,r,val});//把区间重新插回去 } inline void Xor(ll l,ll r) { set ::iterator itr=split(r+1),itl=split(l);//切出最左边的区间和最右边的区间 for(set ::iterator i=itl;i!=itr;++i)i->val^=1;//对于每一个区间权值直接异或 } inline ll get_ans() { for(set ::iterator i=s.begin();i!=s.end();++i) if(i->val==0)return i->l; return (*--s.end()).r+1; } int main() { read(m); s.insert((node){1,inf,0}); for(rint i=1,ty;i<=m;++i) { ll lb,rb;read(ty); scanf("%lld %lld",&lb,&rb); if(ty==1)change(lb,rb,1); if(ty==2)change(lb,rb,0); if(ty==3)Xor(lb,rb); printf("%lld\n",get_ans()); } return 0; }