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;//输出答案
    }
    
  • 相关阅读:
    A Simple Problem with Integers poj 3468 多树状数组解决区间修改问题。
    Fliptile 开关问题 poj 3279
    Face The Right Way 一道不错的尺取法和标记法题目。 poj 3276
    Aggressive cows 二分不仅仅是查找
    Cable master(二分题 注意精度)
    B. Pasha and String
    Intervals poj 1201 差分约束系统
    UITextField的快速基本使用代码块
    将UIImage转换成圆形图片image
    color转成image对象
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu4585.html
Copyright © 2011-2022 走看看