zoukankan      html  css  js  c++  java
  • 【BZOJ2674】Attack(整体二分+树状数组套线段树)

    点此看题面

    大致题意: 一个二维平面上有(n)个带权点,要求支持两种操作:交换两点点权;询问一个区域的第(k)小值。

    前言

    可怕的树套树。。。

    对着一个点调了二十几分钟,不过幸好调过这个点之后就过了,还是挺舒服的。

    大致思路

    考虑第(k)小值这种东西很难搞,而这道题又没有强制在线,因此首先就考虑整体二分。

    而整体二分时,交换可以拆成两个删除、两个插入。

    于是问题就变得很简单了:在二维平面上每次加入或删除一个点,询问一个区域内的点数。

    这就有很多乱七八糟的家伙可以维护了:树状数组套线段树、二维线段树、(KD-Tree)。。。

    想到很久没有写过树套树了,于是写了一发树状数组套线段树,感觉其实也不会很难写。

    代码

    #pragma GCC optimize(2)
    #pragma GCC optimize("inline")
    #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 60000
    #define M 10000
    #define SZ (N+4*M)
    #define LN 20
    #define swap(x,y) (x^=y^=x^=y)
    using namespace std;
    int n,m,cnt,X[N+5],Y[N+5],V[N+5];struct Data
    {
    	int p,k,x,y,a,b;I Data() {p=k=x=y=a=b=0;}
    	I Data(CI i,CI t,CI p,CI q):p(i),k(t),x(p),y(q){}
    	I Data(CI i,CI t,CI p,CI q,CI u,CI v):
    	p(i),k(t),x(p),y(q),a(u),b(v){x>a&&swap(x,a),y>b&&swap(y,b);}
    }s[SZ+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 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=(x<<3)+(x<<1)+(c&15),D);}
    		Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    		I void readc(char& x) {W(isspace(x=tc()));}
    		Tp I void writeln(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);pc('
    ');}
    		I void writes(Con string& x) {for(RI i=0,l=x.length();i^l;++i) pc(x[i]);}
    		I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
    }F;
    struct Discretization//离散化
    {
    	int dc,dv[N+2*M+5];I int& operator [] (CI x) {return dv[x];}
    	I void Init(CI x) {sort(dv+1,dv+x+1),dc=unique(dv+1,dv+x+1)-dv-1;}
    	I int operator () (CI x) {return lower_bound(dv+1,dv+dc+1,x)-dv;}
    }Dx,Dy,Dv;
    class SegmentArray//树状数组套线段树
    {
    	private:
    		int Rt[N+2*M+5];
    		class Segment
    		{
    			private:
    				#define PU(x) (O[x].V=O[O[x].S[0]].V+O[O[x].S[1]].V)
    				#define LT O[rt].S[0],l,mid
    				#define RT O[rt].S[1],mid+1,r
    				int Nt;struct node {int V,S[2];}O[N*LN*LN+5];
    			public:
    				I void U(CI x,CI v,int& rt,CI l=1,CI r=Dy.dc)//单点修改
    				{
    					if(!rt&&(rt=++Nt),l==r) return (void)(O[rt].V+=v);int mid=l+r>>1;
    					x<=mid?U(x,v,LT):U(x,v,RT),PU(rt);
    				}
    				I int Q(CI L,CI R,CI rt,CI l=1,CI r=Dy.dc)//区间查询
    				{
    					if(!rt||(L<=l&&r<=R)) return O[rt].V;int mid=l+r>>1;
    					return (L<=mid?Q(L,R,LT):0)+(R>mid?Q(L,R,RT):0);
    				}
    		}S;
    		I int Q(RI x,CI l,CI r) {RI t=0;W(x) t+=S.Q(l,r,Rt[x]),x-=x&-x;return t;}
    	public:
    		I void U(RI x,CI y,CI v) {W(x<=Dx.dc) S.U(y,v,Rt[x]),x+=x&-x;}//单点修改
    		I int Q(CI x,CI y,CI a,CI b) {return Q(a,y,b)-Q(x-1,y,b);}//矩形查询
    }T;
    class WholeSolver//整体二分
    {
    	private:
    		Data sl[SZ+5],sr[SZ+5];
    	public:
    		int ans[M+5];
    		I void Solve(CI l,CI r,CI L,CI R)
    		{
    			RI i,tl=0,tr=0,fl=0,fr=0;if(l==r||L>R) {for(i=L;i<=R;++i) ans[s[i].p]=l;return;}//处理边界
    			RI t,mid=l+r>>1;for(i=L;i<=R;++i)
    			{
    				if(!s[i].p)//对于修改
    				{
    					(abs(s[i].k)<=mid?T.U(s[i].x,s[i].y,s[i].k>0?1:-1),sl[++tl]:sr[++tr])=s[i];
    					continue;
    				}
    				((t=T.Q(s[i].x,s[i].y,s[i].a,s[i].b))>=s[i].k?//对于询问
    					fl=1,sl[++tl]:(fr=1,s[i].k-=t,sr[++tr]))=s[i];
    			}
    			for(i=L;i<=R;++i) !s[i].p&&abs(s[i].k)<=mid&&(T.U(s[i].x,s[i].y,s[i].k>0?-1:1),0);//清空
    			for(i=1;i<=tl;++i) s[L+i-1]=sl[i];for(i=1;i<=tr;++i) s[L+tl+i-1]=sr[i];
    			fl&&(Solve(l,mid,L,L+tl-1),0),fr&&(Solve(mid+1,r,L+tl,R),0);//递归
    		}
    }S;
    int main()
    {
    	RI i,x,y,a,b,v;for(F.read(n,m),i=1;i<=n;++i)//读入点
    		F.read(X[i],Y[i],V[i]),Dx[i]=X[i],Dy[i]=Y[i],Dv[i]=V[i];//离散化数组记下对应信息
    	for(Dv.Init(n),i=1;i<=n;++i) s[++cnt]=Data(0,V[i]=Dv(V[i]),X[i],Y[i]);//把初始点视作修改
    	RI Qt=0,p,q;char op;for(i=1;i<=m;++i)//读入操作
    		F.readc(op),F.read(x,y),op=='Q'?F.read(a,b,v),++Qt,s[++cnt]=
    			Data(Qt,v,Dx[n+2*Qt-1]=x,Dy[n+2*Qt-1]=y,Dx[n+2*Qt]=a,Dy[n+2*Qt]=b),0//存储询问
    		:(
    			++x,++y,s[++cnt]=Data(0,-V[x],X[x],Y[x]),s[++cnt]=Data(0,-V[y],X[y],Y[y]),//把交换视作两个删除、两个插入
    			s[++cnt]=Data(0,V[y],X[x],Y[x]),s[++cnt]=Data(0,V[x],X[y],Y[y]),swap(V[x],V[y])
    		);
    	for(Dx.Init(n+2*Qt),Dy.Init(n+2*Qt),i=1;i<=cnt;++i)//离散化坐标
    		s[i].x=Dx(s[i].x),s[i].y=Dy(s[i].y),s[i].a=Dx(s[i].a),s[i].b=Dy(s[i].b);
    	for(S.Solve(1,Dv.dc+1,1,cnt),i=1;i<=Qt;++i)//输出答案
    		S.ans[i]<=Dv.dc?F.writeln(Dv[S.ans[i]]):F.writes("It doesn't exist.
    ");//大于最大值说明无解
    	return F.clear(),0;
    }
    
  • 相关阅读:
    html5数字和颜色输入框
    WinForm设置右键菜单
    设置窗体透明C#代码
    C#调用windows api示例
    使用VS GDB扩充套件在VS上远端侦错Linux上的C/C++程序
    javascript系统时间测试题
    博客园学习的好地方
    基于jQuery的自适应图片左右切换
    HTML+CSS代码橙色导航菜单
    ASP.NET使用UpdatePanel实现AJAX
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ2674.html
Copyright © 2011-2022 走看看