zoukankan      html  css  js  c++  java
  • 【BZOJ3600】没有人的算术(替罪羊树+线段树)

    点此看题面

    大致题意: 定义任意数对(>0),数对之间比大小先比第一位、后比第二位,一开始数列全为(0),要求你支持(a_k=(a_x,a_y))和询问区间最大值所在位置两种操作。

    化数对为实数

    直接记录数对显然是不现实,也不可能的。

    考虑到若数对((a,b)<(c,d),(c,d)<(e,f)),则((a,b))必然小于((e,f)),即数对之间的比大小具有传递性

    那么我们可以考虑把每个数对按其大小对应成一个实数,而实数之间的比大小就非常方便了,直接用线段树维护就可以处理询问了。

    也就是说,我们只要知道如何化数对为实数,就可以解决整道题了。

    替罪羊树

    考虑到要把每个数对按大小对应成一个数,可以用平衡树

    具体地,就是对于每个点表示一个数对,并确定一个值域区间([L,R]),设(MID=(L+R)/2),则(MID)就是这个点代表的数对所对应的数,而这个点左儿子的值域区间就是([L,MID]),右儿子的值域区间就是([MID,R])

    特殊地,根节点的值域区间为([0,10^9])(其实直接([0,1])也问题不大,注意要开(double))。

    但是要注意,如果选取(Treap,Splay)等会旋转的平衡树,我们所维护的值域区间就会乱套,因此只能采取替罪羊树,而它在重构时可以暴力重新赋值。

    (关于替罪羊树,可参考我的这篇博客

    注意到上面说到的重新赋值,也就是说,一个点对它所对应的实数可能是会发生变化的。

    因此,在数对及线段树中,我们应该记录的是这个数对在平衡树中对应的节点编号,而非其具体值,在比大小时则可以通过调用得到其具体值进行比较(我一开始就因为这个调了很久)。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100000
    #define M 500000
    #define DB long double
    #define RD Reg DB
    #define CD Con DB&
    using namespace std;
    int n,a[N+5];DB p[M+5];
    class FastIO
    {
    	private:
    		#define FS 100000
    		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
    		#define pc(c) (C==E&&(clear(),0),*C++=c)
    		#define tn (x<<3)+(x<<1)
    		#define D isdigit(c=tc())
    		int T;char c,*A,*B,*C,*E,FI[FS],FO[FS],S[FS];
    	public:
    		I FastIO() {A=B=FI,C=FO,E=FO+FS;}
    		Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
    		I void readc(char& x) {W(isspace(x=tc()));}
    		Tp I void write(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);}
    		Tp I void writeln(Con Ty& x) {write(x),pc('
    ');}
    		I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
    }F;
    struct data
    {
    	int x,y;I data(CI a=0,CI b=0):x(a),y(b){}
    	I bool operator < (Con data& o) Con {return p[x]!=p[o.x]?p[x]<p[o.x]:p[y]<p[o.y];}
    	I bool operator == (Con data& o) Con {return x==o.x&&y==o.y;}
    };
    class ScapegoatTree//替罪羊树
    {
    	private:
    		#define alpha 0.75
    		#define PU(x) (O[x].Sz=O[O[x].S[0]].Sz+O[O[x].S[1]].Sz+1)
    		#define Balance(x) (alpha*O[x].Sz>1.0*max(O[O[x].S[0]].Sz,O[O[x].S[1]].Sz))
    		#define ReBuild(x,L,R) (cnt=0,Tr(x),SetUp(x,1,cnt,L,R))
    		int rt,tot,cnt,cur[M+5];
    		struct node {int Sz,S[2];data V;}O[M+5];
    		I int ins(int& rt,CD L,CD R,Con data& v)//插入一个新数对,返回节点编号
    		{
    			if(!rt) return O[rt=++tot].V=v,p[rt]=(L+R)/2,O[rt].Sz=1,rt;if(O[rt].V==v) return rt;
    			RD MID=(L+R)/2;RI t;return t=v<O[rt].V?ins(O[rt].S[0],L,MID,v):ins(O[rt].S[1],MID,R,v),PU(rt),t;
    		}
    		I void chk(int& rt,CD L,CD R,Con data& v)//检验是否平衡
    		{
    			if(!rt) return;if(!Balance(rt)) return ReBuild(rt,L,R);if(O[rt].V==v) return;
    			RD MID=(L+R)/2;v<O[rt].V?chk(O[rt].S[0],L,MID,v):chk(O[rt].S[1],MID,R,v);
    		}
    		I void Tr(CI x) {x&&(Tr(O[x].S[0]),cur[++cnt]=x,Tr(O[x].S[1]),0);}//中序遍历,存下节点
    		I void SetUp(int& rt,CI l,CI r,CD L,CD R)//重构
    		{
    			RI mid=l+r>>1;RD MID=(L+R)/2;p[rt=cur[mid]]=MID,//重新赋值
    			l<mid?(SetUp(O[rt].S[0],l,mid-1,L,MID),0):(O[rt].S[0]=0),//处理左儿子
    			r>mid?(SetUp(O[rt].S[1],mid+1,r,MID,R),0):(O[rt].S[1]=0),PU(rt);//处理右儿子
    		}
    	public:
    		I int Ins(CI x,CI y) {RI t;data w(x,y);return t=ins(rt,0,1e9,w),chk(rt,0,1e9,w),t;}
    		#undef PU
    }T;
    class SegmentTree//线段树
    {
    	private:
    		#define P CI l=1,CI r=n,CI rt=1
    		#define L l,mid,rt<<1
    		#define R mid+1,r,rt<<1|1
    		#define mp make_pair
    		#define fir first
    		#define sec second
    		#define Max(X,Y) (p[a[X]]>=p[a[Y]]?X:Y)
    		#define PU(x) (Mx[x]=Max(Mx[x<<1],Mx[x<<1|1]))
    		int Mx[N<<2];
    	public:
    		I void Build(P)//建树
    		{
    			if(l==r) return (void)(Mx[rt]=l);RI mid=l+r>>1;Build(L),Build(R),PU(rt);
    		}
    		I void Upt(CI x,P)//修改,实际上只是刷新一下这个点到根路径上的信息
    		{
    			if(l==r) return;RI mid=l+r>>1;x<=mid?Upt(x,L):Upt(x,R),PU(rt);
    		}
    		I int Qry(CI tl,CI tr,P)//询问
    		{
    			if(tl<=l&&r<=tr) return Mx[rt];RI mid=l+r>>1;RI t,res=0;
    			tl<=mid&&(t=Qry(tl,tr,L),res?(res=Max(res,t)):(res=t)),
    			tr>mid&&(t=Qry(tl,tr,R),res?(res=Max(res,t)):(res=t));return res;
    		}
    }S;
    int main()
    {
    	RI Qt,i,l,r,k;char op;F.read(n),F.read(Qt),S.Build();
    	for(T.Ins(0,0),i=1;i<=n;++i) a[i]=1;//初始化
    	W(Qt--) F.readc(op),F.read(l),F.read(r),//读入
    		op=='C'?F.read(k),a[k]=T.Ins(a[l],a[r]),S.Upt(k):F.writeln(S.Qry(l,r));//处理
    	return F.clear(),0;
    }
    
  • 相关阅读:
    北极星杯 awd复现
    5,linux入门到上手-文件与文件系统的压缩,打包与备份
    4,linux入门到上手-文件与目录相关操作
    ctf中 preg_match 绕过技术 | 无字母数字的webshell
    巅峰极客 2019部分题解 writeup
    使用python爬去中国最好大学排名2016年
    python进制转换
    python应用-爬取猫眼电影top100
    3,linux入门到上手-文件权限管理与配置
    DNS、域、域名及FQDN 概念
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ3600.html
Copyright © 2011-2022 走看看