前言
我在模拟赛中把单 \(\log\) 实现为了双 \(\log\),即对于每个线段树节点开了四个优先队列,获得了 \(0\) 分超空的好成绩。
思路
我们先把每次操作给出的区间 \(r-1\)。
首先分析发现,如果存在不交区间,则优先选不交区间,这样就有 \(0\) 个不合法的。
否则,剩下的区间一定都包含一段区间,我们从中选两个即可。
大体思路如上,接下来是具体实现:
我们对于每个点 \(i\) 开四个优先队列,即两个双堆支持加入删除,分别代表左端点为 \(i\) 时最小右端点和右端点为 \(i\) 时最大左端点,这样对于第二种情况,若所有区间都交与 \(l,r\),则只需要找左端点为 \(l\) 的最小右端点和右端点为 \(r\) 的最大左端点,这样求出的就是最小区间。
否则我们考虑使用线段树,我们对于每个线段树节点记录右端点在这个区间的,最大左端点 \(fl\),和左端点在这个区间的,最小右端点 \(fr\),对于每次合并先令答案为两个儿子的答案,然后在存在的情况下尝试用右儿子的 \(fr\) 减去左儿子的 \(fl\) 加一更新,最后答案就是根节点的 \(ans\)。
我们顺便用线段树区间加减求最大值就能知道剩余区间是否全部都交与一段区间,然后分别处理即可。
code
#include<bits/stdc++.h>
#define ls (p<<1)
#define rs ((p<<1)+1)
#define mid ((c[p].l+c[p].r)>>1)
using namespace std;
namespace IO
{template<typename T>void read(T &_x){_x=0;int _f=1;char ch=getchar();while(!isdigit(ch)) _f=(ch=='-'?-1:_f),ch=getchar();while(isdigit(ch)) _x=_x*10+(ch^48),ch=getchar();_x*=_f;}template<typename T,typename... Args>void read(T &_x,Args&...others){Read(_x);Read(others...);}const int BUF=20000000;char buf[BUF],to,stk[32];int plen;#define pc(x) buf[plen++]=x#define flush(); fwrite(buf,1,plen,stdout),plen=0;template<typename T>inline void print(T x){if(!x){pc(48);return;}if(x<0) x=-x,pc('-');for(;x;x/=10) stk[++to]=48+x%10;while(to) pc(stk[to--]);}
}
using namespace IO;
const int N = 1e6+10,M = 1e6+2;
int n,l,r,id,mi,mx,sum,k,x,y;
char ch;
struct w1
{priority_queue<int>p,p1,P,P1;//p:左端点为 i 时最小右端点//p1:相反
}a[N];
struct w
{int l,r,ans,mx,ad,fl,fr;//shan chu xu yao shuang dui shan chu//p:zuo duan dian wei i,zui xiao you duan dian//p1:zui da zuo duan dian
}c[N<<2];
inline void push(int p)
{c[ls].mx += c[p].ad,c[ls].ad += c[p].ad;c[rs].mx += c[p].ad,c[rs].ad += c[p].ad;c[p].ad = 0;
}
void build(int p,int l,int r)
{c[p].ans = 1e9; c[p].fl = 0,c[p].fr = -1e9; c[p].l = l,c[p].r = r;if(l == r) {a[l].p.push(0),a[l].p1.push(-1e9);return;}build(ls,l,mid),build(rs,mid+1,r);
}
void change_l(int p,int l)
{if(c[p].l == l && c[p].r == l) {if(id == 1) a[l].p1.push(-k);else a[l].P1.push(-k);while(!a[l].P1.empty() && a[l].p1.top() == a[l].P1.top()) a[l].P1.pop(),a[l].p1.pop();//双堆进行删除c[p].fr = a[l].p1.top();return;}if(l <= mid) change_l(ls,l);else change_l(rs,l);c[p].ans = min(c[ls].ans,c[rs].ans);c[p].fl = max(c[ls].fl,c[rs].fl);c[p].fr = max(c[ls].fr,c[rs].fr);//由于我们加了-号,变为取max if(c[ls].fl != 0 && c[rs].fr != -1e9) c[p].ans = min(c[p].ans,-c[rs].fr-c[ls].fl+1);
}
void change_r(int p,int l)
{if(c[p].l == l && c[p].r == l) {if(id == 1) a[l].p.push(k);else a[l].P.push(k);while(!a[l].P.empty() && a[l].p.top() == a[l].P.top()) a[l].P.pop(),a[l].p.pop();c[p].fl = a[l].p.top();return;}if(l <= mid) change_r(ls,l);else change_r(rs,l);c[p].ans = min(c[ls].ans,c[rs].ans);c[p].fl = max(c[ls].fl,c[rs].fl);c[p].fr = max(c[ls].fr,c[rs].fr);//同理 if(c[ls].fl != 0 && c[rs].fr != -1e9) c[p].ans = min(c[p].ans,-c[rs].fr-c[ls].fl+1);
}
void change1(int p)
{if(l <= c[p].l && c[p].r <= r)//区间加/减 {c[p].mx += k,c[p].ad += k;return;}if(c[p].ad != 0) push(p);if(l <= mid) change1(ls);if(mid < r) change1(rs);c[p].mx = max(c[ls].mx,c[rs].mx);
}
int query_l(int p)//找左端点
{if(c[p].l == c[p].r) return p;if(c[p].ad != 0) push(p);if(c[ls].mx == sum) return query_l(ls);else return query_l(rs);
}
int query_r(int p)//找右端点
{if(c[p].l == c[p].r) return p;if(c[p].ad != 0) push(p);if(c[rs].mx == sum) return query_r(rs);else return query_r(ls);
}
signed main()
{
// freopen("stone.in","r",stdin);
// freopen("stone.out","w",stdout);read(n);build(1,1,M);while(n--){cin >> ch; read(l),read(r); r--;if(ch == 'A'){sum++;id = 1; k = r,change_l(1,l); k = l,change_r(1,r);k = 1,change1(1);}else{sum--;id = 2; k = r,change_l(1,l); k = l,change_r(1,r);k = -1,change1(1);}l = query_l(1);if(c[l].mx != sum) print(c[1].ans),pc('\n');//有不交的 else //全交 {r = query_r(1);l = c[l].l,r = c[r].l; print(-a[l].p1.top()-a[r].p.top()+1),pc('\n');}}flush();return 0;
}