• 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;
    }
    
  • 相关阅读:
    bootstrap 弹出框(Popover)插件 修改title等属性选项值
    dedecms 搬家流程
    jQuery ui 百叶窗blind方向设置
    css 优先级
    dedecms 标签
    dedecms 建站相关问题
    css 透明度使用
    css 边框使用
    css 阴影使用
    js 常用判断
  • 原文地址:https://www.cnblogs.com/Paulliant/p/10185235.html
Copyright © 2020-2023  润新知