P6272 没有人的算术
https://darkbzoj.tk/problem/3600
https://www.luogu.com.cn/problem/P6272
替罪羊树动态标号+线段树
这个东西应该是叫动态标号吧,反正我看别的大佬是这样说的
题意:
定义了新的一种数,递归的定义为:\((x,y)\) 或 \(0\),其中 \(x,y\) 也是这种数
对于两个这种数大小的比较:若 \(a<b\) 或 \(a=b,c<d\),则 \((a,b)<(c,d)\);若 \(a=b,c=d\) 则 \((a,b)=(c,d)\)
给定一个长度为 \(n\) 的这种数的序列 \(a\),初始全是 \(0\),有 \(m\) 个操作,分为两种:
C l r k
:将 \(a_k\) 赋值为 \((a_l,a_r)\)Q l r
:查询 \(a_l,\ldots a_r\) 中最小的数,如果存在多个同样小的数,将编号最小的视为最小。输出这个编号
\(n\le 10^5,m\le 5\cdot 10^5\)
如果我们能较快的比较两个数的大小,那直接一个线段树单点查询区间维护最大就完了
但如果按照定义来比较,显然复杂度爆掉,那么下面考虑如何更快的比较
可以把每一个题目里的数都用一个实数来表达
首先要解决的是,如何确定这个实数的值。
因为我们不知道后面插入的数有多少比他大,多少比他小,所以可以把它们放到一颗二叉树上,每个节点代表一个权值的区间 \((l,r)\),然后这个节点上的数对权值就是 \(\dfrac{l+r}{2}\)
当插入一个数对时,如果这个数对比当前节点的数对小,就近左子树,权值区间 \((l,\frac{l+r}{2})\),反之进入右子树,权值区间 \((\frac{l+r}{2},r)\)
一直递归到节点为空,或当前节点存在且它代表的数对和插入的数对相等,就返回这个节点
但这样,如果退化成一个链,那每次除以二,精度还是不够用
而这里又涉及权值的区间的问题,不能用基于旋转或分裂合并的平衡树,所以应该用替罪羊树
每次暴力重构的时候,重新为每个节点赋上权值的区间,还有代表这个节点数对的实数
下面就是实现细节上的问题了
至于如何在一个节点保存一个数对,我们在节点的结构体里存两个指针,由于一个数对实际上是由两个保存在其它节点的数对组成,所以这两个指针也就分别指向组成它的两个节点
此外,还需要一个 pos
指针数组,\(pos_i\) 指向 \(a_i\) 所对应的数对在替罪羊树上的节点
然后就可以借助这个数组在线段树上进行 pushup 了
只于一开始所有数都为 \(0\),就先往替罪羊树里插入一个那两个指针都是 null 的节点,也就是根,然后所有 \(pos_i\) 一开始都指向根就行了
屎山代码(写了好几个小时,而且是真的丑):
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
#include<iomanip>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
register int x=0;register int y=1;
register char c=std::getchar();
while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
return y?x:-x;
}
#define N 500005
int n,m;
struct GOAT{
#define alpha 0.7
struct tr;
struct data{
tr *l,*r;
inline int operator <(const data &x){
return l->a==x.l->a?r->a<x.r->a:l->a<x.l->a;
}
inline int operator==(const data &x){
return l->a==x.l->a&&r->a==x.r->a;
}
};
struct tr{
tr *ls,*rs;
int size;
data val;
double a;
}*root,*null,**badtag,dizhi[N],*nodes[N],*pos[N];
double badtagL,badtagR;
int tot,node;
inline void init(){
null=&dizhi[0];
null->val=(data){0,0};
null->size=0;
root=null;
}
inline int isbad(tr *tree){
return tree->ls->size>tree->size*alpha+5||tree->rs->size>tree->size*alpha+5;
}
void dfs(tr *tree){
if(tree->ls!=null) dfs(tree->ls);
nodes[++node]=tree;
if(tree->rs!=null) dfs(tree->rs);
}
tr* build(int l,int r,double L,double R){
if(l>r) return null;
double MID=(L+R)/2;
if(l==r){
nodes[l]->ls=nodes[l]->rs=null;
nodes[l]->size=1;
nodes[l]->a=MID;
return nodes[l];
}
int mid=(l+r)>>1;
nodes[mid]->ls=build(l,mid-1,L,MID);nodes[mid]->rs=build(mid+1,r,MID,R);
nodes[mid]->size=1+nodes[mid]->ls->size+nodes[mid]->rs->size;
nodes[mid]->a=MID;
return nodes[mid];
}
inline void rebuild(tr *&tree){
node=0;dfs(tree);
tree=build(1,node,badtagL,badtagR);
}
tr* insert(tr *&tree,double l,double r,data k){
double mid=(l+r)/2;
if(tree==null){
tree=&dizhi[++tot];
tree->ls=tree->rs=null;
tree->val=k;
tree->a=mid;
tree->size=1;
return tree;
}
if(k==tree->val) return tree;
tree->size++;
tr* ret;
if(k<tree->val) ret=insert(tree->ls,l,mid,k);
else ret=insert(tree->rs,mid,r,k);
if(isbad(tree)) badtag=&tree,badtagL=l,badtagR=r;
return ret;
}
inline tr* Insert(data k){
badtag=NULL;
tr *ret=insert(root,1,1e9,k);
if(badtag) rebuild(*badtag);
return ret;
}
#undef alpha
}B;
struct SEG{
struct tr{
tr *ls,*rs;
int pos;
}dizhi[N],*root=&dizhi[0];
int tot;
void build(tr *tree,int l,int r){
if(l==r) return tree->pos=l,void();
tree->ls=&dizhi[++tot];tree->rs=&dizhi[++tot];
int mid=(l+r)>>1;
build(tree->ls,l,mid);build(tree->rs,mid+1,r);
tree->pos=l;
}
void change(tr *tree,int l,int r,int pos){
if(l==r) return;
int mid=(l+r)>>1;
if(pos<=mid) change(tree->ls,l,mid,pos);
else change(tree->rs,mid+1,r,pos);
tree->pos=B.pos[tree->ls->pos]->a>=B.pos[tree->rs->pos]->a?tree->ls->pos:tree->rs->pos;
}
int get(tr *tree,int l,int r,int ql,int qr){
if(ql<=l&&r<=qr) return tree->pos;
int mid=(l+r)>>1;
if(qr<=mid) return get(tree->ls,l,mid,ql,qr);
if(ql>mid) return get(tree->rs,mid+1,r,ql,qr);
int L=get(tree->ls,l,mid,ql,qr),R=get(tree->rs,mid+1,r,ql,qr);
return B.pos[L]->a>=B.pos[R]->a?L:R;
}
}A;
int main(){
n=read();m=read();
B.init();
B.Insert((GOAT::data){B.null,B.null});
A.build(A.root,1,n);
for(reg int i=1;i<=n;i++) B.pos[i]=B.root;
reg int l,r,k;char c;
while(m--){
c=getchar();
while(c!='Q'&&c!='C') c=getchar();
l=read();r=read();
if(c=='C'){
k=read();
B.pos[k]=B.Insert((GOAT::data){B.pos[l],B.pos[r]});
A.change(A.root,1,n,k);
}
else printf("%d\n",A.get(A.root,1,n,l,r));
}
return 0;
}