zoukankan      html  css  js  c++  java
  • 2019-2020 ICPC Asia Hong Kong. H. Hold the Line(线段树 二分)

    题目链接

    这个数据范围有毒。。
    这种题记得用fread。


    (Description)
    (n)个位置,最初每个位置上没有数。
    (m)次操作,操作有两种:

    1. 在一个没有数的位置(x)加一个数(v)
    2. 给定(x),询问([l,r])中与(x)相差最小的数,即(min_{vin[l,r]}|x-v|)

    (nleq 5 imes10^5, mleq 10^6)

    (Solution)
    最简单的是线段树套set,(O(mlog^2n))+大常数所以过不了。
    Sol 1
    考虑离线,把询问按(r)排序,每次用在(r)位置插入的值更新,然后求以(r)为右端点的询问。

    设此时的一个询问为([L,R],x),询问编号为(id)
    设已经插入的值为(v_i),对应的操作编号为(i),插入位置为(pos_ileq R)
    (v_i)能更新询问当且仅当(pos_igeq L)(ilt id)

    将询问拆成,找最大的(v_i)满足(v_ileq x)(pos_igeq L)(ilt id),和找最小的(v_i)满足(v_igeq x)(pos_igeq L)(ilt id)
    对第一种询问,只需考虑([1,x])中,最靠右的满足(pos_igeq L)(ilt id)(i)是哪个。不妨考虑用一个线段树,对每个节点([l,r]),判断(lleq v_ileq r)时是否有符合条件的(i),如果有就继续先找右儿子再找左儿子,如果该区间没有就return。
    也就是用一个线段树维护所有当前的(v_i),节点([l,r])保存所有(lleq v_ileq r)(i)
    然后如何判断某节点的(v_i)中是否存在(i)使(pos_igeq L)(ilt id)
    注意到我们是按(pos)从小到大更新插入的值,所以后插入的值(pos)一定大于之前的。而节点中有必要保留的点,是要么(pos)更大、要么(i)更小的点。所以如果在某节点插入(v_i)时,(i)小于该节点中另一个值的操作编号,那个值就可以删掉。
    容易发现这样每个节点都是一个单调队列,从队首到队尾,编号(i)递增,(pos)递增。
    所以每个节点的查询,只需要二分第一个(posgeq L)的值,然后判断其编号(i)是否有(ilt id)(或最后一个(ilt id)的值,然后判断是否其(posgeq L))。
    这样每次查询复杂度(O(log^2n))。每次插入值,就更新根节点到该值整条链上的单调队列,复杂度(O(log n))

    所以复杂度(O(nlog n+mlog^2n))
    注意空间也是(O(nlog n))的,刚开始要建树,根据每个节点上数的个数(前缀和算)分配一下其单调队列的空间。

    Sol 2
    在线的话可以分块。每个块因为最多有(sqrt n)个数,所以在每次插入时,暴力将这个数放到对应位置,使每块里的数保持有序。
    查询时,边界暴力,整块中因为块内有序,可以二分第一个小于等于(x)的数(A[p])(A[p],A[p+1])即为可能答案。
    复杂度(O(msqrt n+msqrt nlog n))。和std跑的差不多快。。(反正比我快/kk)


    //2292ms	116300KB
    #include <bits/stdc++.h>
    #define pc putchar
    #define gc() getchar()
    #define MAXIN 500000
    //#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
    #define pb emplace_back
    typedef long long LL;
    const int N=5e5+5,M=1e6+6,Q=1e7+1e6+5/*队列最少的空间,1e7还是太小*/;
    
    int A[M],sum[M],Ans[M],qid[N];
    char IN[MAXIN],*SS=IN,*TT=IN;
    struct Quries
    {
    	int L,pos,val;
    }q[M];
    std::vector<int> vec[N];
    struct Segment_Tree
    {
    	#define S M<<2	//值域为M!
    	#define ls rt<<1
    	#define rs rt<<1|1
    	#define lson l,m,ls
    	#define rson m+1,r,rs
    	int now,res,qL,qid,pos[M],L[S],R[S],q[Q];
    	#undef S
    	void Build(int l,int r,int rt)
    	{
    		L[rt]=now, R[rt]=now-1;	//初始化该节点的队首和队尾 
    		now+=sum[r]-sum[l-1];	//分配该节点的空间 
    		if(l==r)
    		{
    			pos[l]=rt;			//记一下每个值叶节点的位置 
    			return;
    		}
    		int m=l+r>>1;
    		Build(lson), Build(rson);
    	}
    	void Update(int p,int id,int val)
    	{
    		for(int rt=pos[val]; rt; rt>>=1)	//记录叶节点位置,自底向上for就可以了 
    		{
    			int h=L[rt], t=R[rt];
    			while(h<=t && q[t]>id) --t;
    			q[++t]=id, R[rt]=t;
    		}
    	}
    	bool Check(int rt)
    	{
    		int l=L[rt],r=R[rt],mid;
    		while(l<=r)
    		{
    			mid=l+r>>1;
    			if(q[mid]<qid && ::q[q[mid]].pos>=qL) return 1;
    			if(q[mid]<qid) l=mid+1;
    			else r=mid-1;
    		}
    		return 0;
    //		while(l<r)
    //		{
    //			mid=l+r+1>>1;
    //			if(q[mid]<qid) l=mid;
    //			else r=mid-1;
    //		}
    //		return q[l]<qid && ::q[q[l]].pos>=qL;
    	}
    	void QueryL(int l,int r,int rt,int qR)
    	{
    		if(res||L[rt]>R[rt]) return;	//! 区间里没有数记得return 
    		if(r<=qR)
    		{
    			if(!Check(rt)) return;	//先找到对应区间,再Check,少Check一些是不是会快 
    			if(l==r) {res=l; return;}	//当然不合法区间不会DFS到叶节点 
    		}
    		int m=l+r>>1;
    		if(m<qR) QueryL(rson,qR);
    		QueryL(lson,qR);
    	}
    	void QueryR(int l,int r,int rt,int qL)
    	{
    		if(res||L[rt]>R[rt]) return;	//! 区间里没有数记得return 
    		if(qL<=l)
    		{
    			if(!Check(rt)) return;
    			if(l==r) {res=l; return;}
    		}
    		int m=l+r>>1;
    		if(qL<=m) QueryR(lson,qL);
    		QueryR(rson,qL);
    	}
    }T;
    
    inline int read()
    {
    	int now=0,f=1; char c=gc();
    	for(;!isdigit(c);c=='-'&&(f=-1),c=gc());
    	for(;isdigit(c);now=now*10+c-48,c=gc());
    	return now*f;
    }
    void Query(int id,int cnt)
    {
    	int L=q[id].L,val=q[id].val,ans=2e9;
    	T.qL=L, T.qid=id;
    
    	T.res=0, T.QueryL(1,cnt,1,val);
    	if(T.res) ans=A[val]-A[T.res];
    	T.res=0, T.QueryR(1,cnt,1,val);
    	if(T.res) ans=std::min(ans,A[T.res]-A[val]);
    
    	Ans[id]=ans==2e9?-1:ans;
    }
    
    int main()
    {
    	for(int Ts=read(); Ts--; )
    	{
    		int n=read(),m=read(),cnt=m;
    		for(int i=1; i<=m; ++i)
    		{
    			if(!read()) q[i]=Quries{0,read(),read()};
    			else q[i]=Quries{read(),read(),read()};
    			A[i]=q[i].val;
    		}
    		std::sort(A+1,A+1+m), cnt=std::unique(A+1,A+1+m)-A-1;	//值的个数为m不是n! 
    
    		memset(sum,0,cnt+1<<2), memset(qid,0,n+1<<2);
    		for(int i=1; i<=n; ++i) std::vector<int>().swap(vec[i]);
    
    		for(int i=1; i<=m; ++i)
    		{
    			int v=std::lower_bound(A+1,A+1+cnt,q[i].val)-A;
    			q[i].val=v;
    			if(!q[i].L) qid[q[i].pos]=i, ++sum[v];
    			else vec[q[i].pos].pb(i);
    		}
    		for(int i=1; i<=cnt; ++i) sum[i]+=sum[i-1];	//注意n,m,cnt含义!
    
    		#define S 1,cnt,1
    		T.now=1, T.Build(S);
    
    		for(int i=1; i<=n; ++i)	//枚举R 
    		{
    			if(qid[i]) T.Update(i,qid[i],q[qid[i]].val);
    			for(auto v:vec[i]) Query(v,cnt);
    		}
    		
    		for(int i=1; i<=m; ++i) q[i].L && printf("%d
    ",Ans[i]);
    	}
    
    	return 0;
    }
    
    ------------------------------------------------------------------------------------------------------------------------
    无心插柳柳成荫才是美丽
    有哪种美好会来自于刻意
    这一生波澜壮阔或是不惊都没问题
    只愿你能够拥抱那种美丽
    ------------------------------------------------------------------------------------------------------------------------
  • 相关阅读:
    tomcat使用redis存储共享session
    dubbo专题」dubbo其实很简单,就是一个远程服务调用的框架(1)
    SpringBoot Test集成测试
    IntelliJ Idea设置护眼浅绿色背景方法
    IDEA使用有道翻译插件
    IDEA Translation插件中有道智云(有道翻译)应用ID,密钥申请教程
    利用grep-console插件使Intellij idea显示多颜色调试日志
    mybatis 整合spring之mapperLocations配置的问题
    idea将maven项目打包成war包的方式,以及使用war包
    maven 常用命令
  • 原文地址:https://www.cnblogs.com/SovietPower/p/14730756.html
Copyright © 2011-2022 走看看