zoukankan      html  css  js  c++  java
  • HDU 5649 DZY Loves Sorting(二分答案+线段树/线段树合并+线段树分割)

    题意

    一个 (1)(n) 的全排列,(m) 种操作,每次将一段区间 ([l,r]) 按升序或降序排列,求 (m) 次操作后的第 (k) 位。

    (1 leq n leq 10^5)

    思路

    两个 (log) 的做法展现了二分答案的强大功能。首先二分枚举第 (k) 位的值,然后将小于等于它的数都变为 (1) ,大于它的数变为 (0) ,线段树可以实现对 (01) 序列快速的排序,按要求进行排序,然后如果第 (k) 位为 (1) 说明这个数小于等于 (k) ,就这样不断二分下来,得到的边界值就是第 (k) 位真实的值。这个做法是离线的,有两个 (log) ,但代码好实现。

    但这道题,有一个 (log) 、在线的做法。考虑每个位置开一棵动点线段树,把这个位置的数扔进线段树,区间的排序直接用线段树合并进行,但是如果区间的某个端点落在某一个完整的区间内,那就会破坏这个区间的单调性,所以还要线段树分割。我们对于一个完整区间,存下是升序还是降序,然后“分割”出需要的元素,线段树分割代码如下:

    void split(int &x,int y,int K,int l,int r)		//y拆前K个给x,合并前将初始x清零(x是一个空树)
    {
    	create(x);
    	if(l==r){sum[x]=sum[y],sum[y]=0;return;}
    	int mid=(l+r)>>1;
    	if(K<=sum[lson[y]])
    	{
    		split(lson[x],lson[y],K,l,mid);
    		sum[x]=sum[lson[x]]+sum[rson[x]];
    		sum[y]=sum[lson[y]]+sum[rson[y]];
    		return;
    	}
    	split(rson[x],rson[y],K-sum[lson[y]],mid+1,r);
    	lson[x]=lson[y],lson[y]=0;
    	sum[x]=sum[lson[x]]+sum[rson[x]];
    	sum[y]=sum[lson[y]]+sum[rson[y]];
    }
    

    和线段树合并的写法大致相同。

    初始有 (nlog n) 个点,每次操作最多分割出 (2log n) 个节点 ,所以空间复杂度为 (O(nlog n))

    合并初始的 (n) 个节点有一个 (nlog n) ,而分割的节点也最多是 (2 nlog n) ,所以时间复杂度也是 (O(nlog n))

    代码

    #include<bits/stdc++.h>
    #define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i)
    #define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i)
    typedef long long LL;
    using namespace std;
    const int N=1e5+5;
    const int NN=N*60;
    bool mmr1;
    int sum[NN],lson[NN],rson[NN];
    int rt[N],tot;
    void build()
    {
    	memset(rt,0,sizeof(rt));
    	sum[tot=0]=lson[0]=rson[0]=0;
    }
    void create(int &k){if(!k)k=++tot,sum[k]=lson[k]=rson[k]=0;}
    void update(int &k,int x,int l,int r)
    {
    	create(k);
    	sum[k]++;
    	if(l==r)return;
    	int mid=(l+r)>>1;
    	if(x<=mid)update(lson[k],x,l,mid);
    	else update(rson[k],x,mid+1,r);
    }
    int query1(int k,int K,int l,int r)
    {
    	if(l==r)
    	{
    		if(sum[k]!=1)return -1;
    		return l;
    	}
    	int mid=(l+r)>>1;
    	if(K<=sum[lson[k]])return query1(lson[k],K,l,mid);
    	else return query1(rson[k],K-sum[lson[k]],mid+1,r);
    }
    int query2(int k,int K,int l,int r)
    {
    	if(l==r)
    	{
    		if(sum[k]!=1)return -1;
    		return l;
    	}
    	int mid=(l+r)>>1;
    	if(K<=sum[rson[k]])return query2(rson[k],K,mid+1,r);
    	else return query2(lson[k],K-sum[rson[k]],l,mid);
    }
    void merge(int &x,int y,int l,int r)						//y并进x 
    {
    	if(!x||!y){x=(x|y);return;}
    	if(l==r){sum[x]+=sum[y];return;}
    	int mid=(l+r)>>1;
    	merge(lson[x],lson[y],l,mid);
    	merge(rson[x],rson[y],mid+1,r);
    	sum[x]=sum[lson[x]]+sum[rson[x]];
    }
    void split1(int &x,int y,int K,int l,int r)		//y拆前K个给x
    {
    	create(x);
    	if(l==r){sum[x]=sum[y],sum[y]=0;return;}
    	int mid=(l+r)>>1;
    	if(K<=sum[lson[y]])
    	{
    		split1(lson[x],lson[y],K,l,mid);
    		sum[x]=sum[lson[x]]+sum[rson[x]];
    		sum[y]=sum[lson[y]]+sum[rson[y]];
    		return;
    	}
    	split1(rson[x],rson[y],K-sum[lson[y]],mid+1,r);
    	lson[x]=lson[y],lson[y]=0;
    	sum[x]=sum[lson[x]]+sum[rson[x]];
    	sum[y]=sum[lson[y]]+sum[rson[y]];
    }
    void split2(int &x,int y,int K,int l,int r)		//y拆后K个给x
    {
    	create(x);
    	if(l==r){sum[x]=sum[y],sum[y]=0;return;}
    	int mid=(l+r)>>1;
    	if(K<=sum[rson[y]])
    	{
    		split2(rson[x],rson[y],K,mid+1,r);
    		sum[x]=sum[lson[x]]+sum[rson[x]];
    		sum[y]=sum[lson[y]]+sum[rson[y]];
    		return;
    	}
    	split2(lson[x],lson[y],K-sum[rson[y]],l,mid);
    	rson[x]=rson[y],rson[y]=0;
    	sum[x]=sum[lson[x]]+sum[rson[x]];
    	sum[y]=sum[lson[y]]+sum[rson[y]];
    }
    set<int>st;
    set<int>::iterator it,it1;
    bool f[N];
    
    int find_leftmost(int x)
    {
    	it=st.upper_bound(x);
    	return *--it;
    }
    int find_rightmost(int x)
    {
    	it=st.upper_bound(x);
    	return (*it)-1;
    }
    bool mmr2;
    
    int main()
    {
     	int T,n,m,K;
    	scanf("%d",&T);
    	while(T--)
    	{
    		build();
    		st.clear();
    		memset(f,0,sizeof(f));
    		scanf("%d%d",&n,&m);
    		FOR(i,1,n)
    		{
    			int x;
    			scanf("%d",&x);
    			update(rt[i],x,1,n);
    		}
    		FOR(i,1,n+1)st.insert(i);
    		
    		while(m--)
    		{
    			int op,l,r;
    			scanf("%d%d%d",&op,&l,&r);
    			int L=find_leftmost(l);
    			if(l!=L)
    			{
    				if(f[L]==0)rt[l]=0,split1(rt[l],rt[L],l-L,1,n);
    				else rt[l]=0,split2(rt[l],rt[L],l-L,1,n);
    				swap(rt[l],rt[L]);
    				f[l]=f[L];
    				st.insert(l);
    			}
    			
    			int R=find_rightmost(r),_R=find_leftmost(r);
    			if(r!=R)
    			{
    				f[r+1]=f[_R];
    				if(f[_R]==0)rt[r+1]=0,split2(rt[r+1],rt[_R],R-r,1,n);
    				else rt[r+1]=0,split1(rt[r+1],rt[_R],R-r,1,n);
    				st.insert(r+1);
    			}
    			
    			f[l]=op;
    			it=st.find(l),it++;
    			while((*it)<=r)
    			{
    				merge(rt[l],rt[*it],1,n);
    				it1=it,it++,st.erase(it1);
    			}
    		}
    		scanf("%d",&K);
    		int x=find_leftmost(K);
    		if(f[x]==0)printf("%d
    ",query1(rt[x],K-x+1,1,n));
    		else printf("%d
    ",query2(rt[x],K-x+1,1,n));
    	}
    	return 0;
    }
    
  • 相关阅读:
    OpenJDK源码研究笔记(十二):JDBC中的元数据,数据库元数据(DatabaseMetaData),参数元数据(ParameterMetaData),结果集元数据(ResultSetMetaDa
    Java实现 LeetCode 257 二叉树的所有路径
    Java实现 LeetCode 257 二叉树的所有路径
    Java实现 LeetCode 257 二叉树的所有路径
    Java实现 LeetCode 242 有效的字母异位词
    Java实现 LeetCode 242 有效的字母异位词
    Java实现 LeetCode 242 有效的字母异位词
    Java实现 LeetCode 241 为运算表达式设计优先级
    Java实现 LeetCode 241 为运算表达式设计优先级
    Java实现 LeetCode 241 为运算表达式设计优先级
  • 原文地址:https://www.cnblogs.com/Paulliant/p/10185235.html
Copyright © 2011-2022 走看看