bzoj 3600 - 没有人的算术
Description
blablabla
开个脑洞:
题目讲了两页跟圣经有关的东东
替罪羊的名字又来自圣经
所以这题是替罪羊
Analysis
第二问求区间最大值用线段树就可以搞定
难度在第一问
不难想到合并时double乱搞搞出一个代表它的大小作为映射
然后当然会爆精度-.-
Solution
1.不难发现,对于将x,y合并成(x,y)的操作中
我们把题目中(l,r)稍微改成(rk[l],rk[r])
x的排名和y的排名都是已知的,所以能很快的找到(x,y)应该排第几
这就是平衡树了
2.但是,这样排名还是难以控制
我们可以在一个点上加上一个控制区域
root控制区域为(l,r)
lc[root]控制(0,mid-1),rc[root]控制(mid+1,r)
root映射到得相对大小就是mid
3.但是,有几个问题:
①一旋转这些控制区间就乱套了
②深度大了跟暴力double是一个样的
那我们就用替罪羊树的优美性质,保持重量平衡,且重构时才修改映射
4.但是,因为排名会变,把(l,r)转化成(rk[l],rk[r])存储是不现实的,改为存(pos[l],pos[r]),pos[l]指向替罪羊中的一个点,rk[pos[l]]才是排名
Notice
1.替罪羊的 深度,字数大小 都是均摊log的
2.算映射mid时小心l+r爆炸
3.重构时虽然映射变了,但其中的相对大小关系不变,是不用修改线段树的
Code
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cctype>
#include <algorithm>
using namespace std;
typedef long long LL;
typedef double db;
const int M=500007;
const db alpha=0.75;
const LL INF=9223372036854775807;
inline int rd(){
int x=0;bool f=1;char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-')f=0;
for(;isdigit(c);c=getchar()) x=x*10+c-48;
return f?x:-x;
}
int n,m;
char s[7];
int pos[M];//pos[i]表示a[i]对应的treap中节点
LL rk[M];//treap中节点映射得到的一个排名
struct node{
int x,y;
node(int xx=0,int yy=0){x=xx;y=yy;}
};
bool operator ==(node x,node y){return rk[x.x]==rk[y.x]&&rk[x.y]==rk[y.y];}
bool operator < (node x,node y){return rk[x.x]<rk[y.x]||(rk[x.x]==rk[y.x]&&rk[x.y]<rk[y.y]);}
struct ScapeGt{
node val[M];//用于treap比较大小
int lc[M],rc[M];
int sz[M];
int root,tot;
int *sgt;
LL ssl,ssr;
int que[M],tque;
bool isbad(int x){
return (db)sz[x]*alpha<(db)max(sz[lc[x]],sz[rc[x]]);
}
void pushup(int x){
sz[x]=1+sz[lc[x]]+sz[rc[x]];
}
void dfs(int x){
if(lc[x]) dfs(lc[x]);
que[++tque]=x;
if(rc[x]) dfs(rc[x]);
}
int build(int l,int r,LL sl,LL sr){
if(l>r) return 0;
int mid=l+r>>1;
LL smid=sl+(sr-sl)/2;
int x=que[mid];
sz[x]=1;
rk[x]=smid;
lc[x]=build(l,mid-1,sl,smid-1);
rc[x]=build(mid+1,r,smid+1,sr);
pushup(x);
return x;
}
void rebuild(int *x,LL l,LL r){
tque=0;
dfs(*x);
*x=build(1,tque,l,r);
}
int insert(int &x,node d,LL l,LL r){
LL mid=l+(r-l)/2;
if(!x){
x=++tot;
val[x]=d;
sz[x]=1;
lc[x]=rc[x]=0;
rk[x]=mid;
return x;
}
if(val[x]==d) return x;
int tp;
if(d<val[x]) tp=insert(lc[x],d,l,mid-1);
else tp=insert(rc[x],d,mid+1,r);
pushup(x);
if(isbad(x)) sgt=&x,ssl=l,ssr=r;
return tp;
}
int ins(node d){
sgt=0;
int x=insert(root,d,0,INF);//前面写-INF算mid会炸掉
if(sgt) rebuild(sgt,ssl,ssr);
return x;
}
}SGT;
struct segtree{
int mx[M];
int gmx(int x,int y){
if(rk[pos[x]]==rk[pos[y]]) return x<y?x:y;
return rk[pos[x]]>rk[pos[y]]?x:y;
}
void pushup(int x){
mx[x]=gmx(mx[x<<1],mx[x<<1|1]);
}
void build(int x,int l,int r){
if(l==r){
mx[x]=l;
return;
}
int mid=l+r>>1;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
pushup(x);
}
void mdf(int x,int l,int r,int to){
if(l==r) return;
int mid=l+r>>1;
if(to<=mid) mdf(x<<1,l,mid,to);
else mdf(x<<1|1,mid+1,r,to);
pushup(x);
}
int get(int x,int l,int r,int tl,int tr){
if(tl<=l&&r<=tr) return mx[x];
int mid=l+r>>1;
if(tl<=mid&&mid<tr)
return gmx(get(x<<1,l,mid,tl,tr),get(x<<1|1,mid+1,r,tl,tr));
if(tl<=mid) return get(x<<1,l,mid,tl,tr);
if(mid<tr) return get(x<<1|1,mid+1,r,tl,tr);
}
}Seg;
int main(){
int i,x,y,z;
n=rd(), m=rd();
SGT.ins(node(0,0));
for(i=1;i<=n;i++) pos[i]=1;
Seg.build(1,1,n);
for(i=1;i<=m;i++){
scanf("%s",s);
if(s[0]=='C'){
x=rd(),y=rd(),z=rd();
node nw=node(pos[x],pos[y]);
pos[z]=SGT.ins(nw);
Seg.mdf(1,1,n,z);
}
else{
x=rd(),y=rd();
printf("%d\n",Seg.get(1,1,n,x,y));
}
}
return 0;
}