zoukankan      html  css  js  c++  java
  • 【洛谷4585】[FJOI2015] 火星商店问题(线段树分治)

    点此看题面

    大致题意:(n)家店,每个商品有一个标价。每天,都可能有某家商店进货,也可能有某人去购物。一个人在购物时,会于编号在区间([L_i,R_i])的商店里挑选一件进货(d_i)天以内的商品使得其标价与(x_i)的异或值最大,同时每家店有一个特殊商品在任何时候都可以选择。对于每名购物者求出这个最大异或值。

    暴力

    一个很暴力的想法,就是线段树套可持久化(Trie)......

    但这貌似是过不了的,因此需要优化。

    线段树分治

    这道题的正解是一个叫做线段树分治的神奇算法。

    考虑对于一次询问,它询问的是一段时间区间内的答案。

    则我们可以考虑开一棵线段树,然后把每个询问都扔到一个以时间为下标的线段树内的对应区间上。

    接下来,考虑离线,在线段树上,用类似于整体二分的思想,维护每一个节点所对应的这段时间内的修改操作。

    然后可以参照前面暴力的想法,用一棵可持久化(Trie)树。对于每一个节点,我们先清空可持久化(Trie)树,再把这个节点的修改对应到可持久化(Trie)树上。随后我们枚举询问,求出每个询问所询问的区间后,就可以求出每个询问的答案了。

    注意一个询问可能扔到线段树可能会被分成多个,因此答案要取(max)

    代码

    #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 LN 20
    #define max(x,y) ((x)>(y)?(x):(y))
    #define Gmax(x,y) (x<(y)&&(x=(y)))
    using namespace std;
    int n,Et,Qt,ans[N+5];
    struct Event {int i,v,t;I bool operator < (Con Event& o) Con {return i<o.i;}}e[N+5];
    struct Query {int l,r,v,x,y,t;}q[N+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);}
    		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;
    class ChairmanTrie//可持久化Trie
    {
    	private:
    		#define ST(x) O[rt1].S[x],O[rt2].S[x],v,d-1
    		int Nt,Vt,Rt[N+5];struct node {int V,S[2];I void Clear() {V=S[0]=S[1]=0;}}O[N*LN<<2];
    		I void Ins(CI rt1,int& rt2,CI v,CI d)//插入新数
    		{
    			O[rt2=++Nt]=O[rt1],++O[rt2].V;if(!~d) return;int t=(v>>d)&1;Ins(ST(t));
    		}
    		I int Qry(CI rt1,CI rt2,CI v,CI d)//求最大异或值
    		{
    			if(!rt2||!~d) return 0;int t=(v>>d)&1;
    			return O[O[rt1].S[t^1]].V^O[O[rt2].S[t^1]].V?Qry(ST(t^1))|(1<<d):Qry(ST(t));
    		}
    	public:
    		I void Insert(CI x) {++Vt,Ins(Rt[Vt-1],Rt[Vt],x,LN);}
    		I int Query(CI l,CI r,CI x) {return Qry(Rt[l-1],Rt[r],x,LN);}
    		I void Clear() {W(Nt) Rt[Nt--]=0;W(Vt) O[Vt--].Clear();}//清空
    }C;
    class SegmentTree
    {
    	private:
    		#define PT CI l=1,CI r=Et,CI rt=1
    		#define LT l,mid,rt<<1
    		#define RT mid+1,r,rt<<1|1
    		#define pb push_back
    		int s[N+5];Event tl[N+5],tr[N+5];vector<int> P[N<<2];
    	public:
    		I void Ins(CI x,CI y,CI p,PT)//扔入询问
    		{
    			if(x<=l&&r<=y) return P[rt].pb(p);int mid=l+r>>1;//开vector存储一个节点上的询问
    			x<=mid&&(Ins(x,y,p,LT),0),y>mid&&(Ins(x,y,p,RT),0);
    		}
    		I void Solve(CI x,CI y,PT)//二分求答案
    		{
    			if(x>y) return;RI i,t,t1,t2,k=0;int mid=l+r>>1;vector<int>::iterator it;
    			for(C.Clear(),i=x;i<=y;++i) s[++k]=e[i].i,C.Insert(e[i].v);//将修改对应到可持久化Trie树上
    			for(it=P[rt].begin();it!=P[rt].end();++it)//枚举这个节点上的询问
    				t1=upper_bound(s+1,s+k+1,q[*it].l-1)-s,t2=upper_bound(s+1,s+k+1,q[*it].r)-s-1,//求出询问在可持久化Trie树上对应的区间
    				t=C.Query(t1,t2,q[*it].v),Gmax(ans[q[*it].t],t);//在可持久化Trie树上询问,更新答案
    			if(l==r) return;for(t1=t2=0,i=x;i<=y;++i) e[i].t<=mid?tl[++t1]=e[i]:tr[++t2]=e[i];
    			for(i=1;i<=t1;++i) e[x+i-1]=tl[i];for(i=1;i<=t2;++i) e[x+i+t1-1]=tr[i];
    			Solve(x,x+t1-1,LT),Solve(x+t1,y,RT);//递归操作
    		}
    }S;
    int main()
    {
    	RI m,i,op,x;for(F.read(n),F.read(m),i=1;i<=n;++i) F.read(x),C.Insert(x);//对于特殊商品
    	W(m--) F.read(op),!op?(F.read(e[++Et].i),F.read(e[Et].v),e[Et].t=Et)://读入修改
    	(
    		F.read(q[++Qt].l),F.read(q[Qt].r),F.read(q[Qt].v),F.read(x),q[Qt].x=max(Et-x,0)+1,q[Qt].y=Et,//读入询问
    		ans[Qt]=C.Query(q[Qt].l,q[Qt].r,q[Qt].v),q[Qt].t=Qt//因为特殊商品随时都可以选,因此初始化答案
    	);
    	for(i=1;i<=Qt;++i) q[i].x<=q[i].y&&(S.Ins(q[i].x,q[i].y,i),0);sort(e+1,e+Et+1),S.Solve(1,Et);//把询问扔入线段树,离线二分求答案
    	for(i=1;i<=Qt;++i) F.writeln(ans[i]);return F.clear(),0;//输出答案
    }
    
  • 相关阅读:
    讲课专用——线段树——最长上升子序列
    讲课专用——线段树——最长连续空位
    讲课专用——线段树——BSS
    求背景图
    致备战noip2018的勇士
    TNS-12547 Linux Error: 104: Connection reset by pe (转载)
    清理Oracle安装目录里的一些日志信息
    tmp_table_size
    11gr2 alert日志中报TNS-12535 TNS-00505原因及解决方法 (转载)
    systemstate dump 介绍
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu4585.html
Copyright © 2011-2022 走看看