线段树+替罪羊树
题意即给出一类嵌套结构的类似pair的“数”,定义它们之间的大小比较为递归比较first和second。(设first和second分别为数的括号里的左右两边)
查区间max,可以考虑线段树,那么重点就是如何快速判断两个数的大小。
暴力比较可以有30分。
注意到后面有两个点的出现数字个数不超过1000,暗示我们可以直接处理出所有数对的大小关系,可以有50分。
然而后面的测试点出现的数字个数有500000个,怎么办?注意到50分的做法实际上在做一件事情:支持任意两个数比较大小。那为什么不直接把这些数字拿来排序呢? 用平衡树即可。有70分。
用平衡树维护时,比较大小需要去找first和second的排名,是logn的。能不能做到像实数那样有一个确切值可以直接比较?我们需要一个从这种“数”到实数的映射关系,使得它们的大小关系能一一对应。用到一个trick。一个平衡树上结点对应区间(l,r),则设它的值为(l+r)/2,类似于中序遍历的感觉,即可O(1)比较大小。还有一个问题,传统平衡树一般需要旋转,旋转之后子树内的值都要重新算,会T。用 重量平衡树即可。替罪羊树修改均摊O(logn),所以均摊下来修改不会超过O(nlogn)个点。
#include<cstdio>
#define N 500005
using namespace std;
namespace runzhe2000
{
int in()
{
int r = 0;
char c = getchar();
while(c<'0' || c>'9')c=getchar();
while(c>='0' && c<='9')r=r*10+c-'0', c=getchar();
return r;
}
int buffcnt, n, m;
struct node *null, *tot, *root, *buff[N], *bad, *badf, *ins, *pos[N];
double badl, badr, alpha = 0.7;
int lim[N];
struct node
{
double val;
int siz;
node *ch[2], *fir, *sec;
bool smaller(node *x, node *y){return x->fir->val < y->fir->val || (y->fir == x->fir && x->sec->val < y->sec->val);}
bool equ(node *x, node *y){return y->fir == x->fir && y->sec == x->sec;}
node *insert(double lv, double rv);
void recycle()
{
if(this == null)return;
ch[0]->recycle();
buff[++buffcnt] = this;
ch[1]->recycle();
}
node *rebuild(int l, int r, double lv, double rv)
{
if(l > r)return null;
int mid = (l+r)>>1;
node *o = buff[mid];
o->val = (lv+rv)/2;
o->ch[0] = rebuild(l,mid-1,lv,o->val);
o->ch[1] = rebuild(mid+1,r,o->val,rv);
o->siz = r-l+1;
return o;
}
}me[N*2];
node *newnode(double lv, double rv)
{
node *o = ++tot;
o->ch[0] = o->ch[1] = null;
o->siz = 1;
o->val = (lv+rv)/2;
return o;
}
node* node::insert(double lv, double rv)
{
if(this == null){ins->val = (lv+rv)/2; return ins;}
if(equ(this,ins))return ins = this;
node *o = smaller(ins, this) ? (ch[0]=ch[0]->insert(lv,val)): ( ch[1]=ch[1]->insert(val,rv));
siz = ch[0]->siz + ch[1]->siz + 1;
if(o->siz > alpha * siz)bad = this, badl = lv, badr = rv;
if(ch[0] == bad || ch[1] == bad)badf = this;
return this;
}
void init()
{
tot = me;
null = ++tot;
null->ch[0] = null->ch[1] = null;
null->siz = null->val = 0;
root = newnode(0, 1);
root->fir = newnode(-1,-1);
root->sec = newnode(-1,-1);
for(int i = 1; i <= n; i++)pos[i] = root;
}
struct seg{int l, r, v;}t[N*5];
void pushup(int x)
{
int lson = x<<1, rson = x<<1|1;
t[x].v = t[ ( pos[t[lson].v]->val >= pos[t[rson].v]->val ? lson : rson ) ].v;
}
void build(int x, int l, int r)
{
t[x].l = l, t[x].r = r;
if(l == r){t[x].v = l; return;}
int mid = (l+r)>>1;
build(x<<1,l,mid); build(x<<1|1,mid+1,r);
pushup(x);
}
int query(int x, int l, int r)
{
if(l <= t[x].l && t[x].r <= r)return t[x].v;
int mid = (t[x].l + t[x].r) >> 1, q1=0, q2=0;
if(l <= mid) q1 = query(x<<1,l,r);
if(mid < r)q2 = query(x<<1|1,l,r);
if(!q1 || !q2)return q1?q1:q2;
return pos[q1]->val >= pos[q2]->val ? q1 : q2;
}
void modi(int x, int p)
{
if(t[x].l==t[x].r)return;
int mid = (t[x].l + t[x].r)>>1;
if(p<=mid)modi(x<<1,p);
else modi(x<<1|1,p);
pushup(x);
}
char op[3];
void main()
{
n=in(), m=in();
init();
build(1,1,n);
for(int i = 1, l, r, k; i <= m; i++)
{
scanf("%s",op);
if(op[0] == 'C')
{
l=in(), r=in(), k=in();
ins = newnode(0, 1);
ins->fir = pos[l];
ins->sec = pos[r];
ins->siz = 1;
bad = null;
root->insert(0, 1);
pos[k] = ins;
if(bad != null)
{
buffcnt = 0;
bad -> recycle();
node *tmp = bad -> rebuild(1, buffcnt, badl, badr);
if(root == bad)root = tmp;
else badf->ch[badf->ch[0] == bad?0:1] = tmp;
}
modi(1, k);
}
else
{
l=in(), r=in();
printf("%d\n",query(1,l,r));
}
}
}
}
int main()
{
runzhe2000::main();
}