zoukankan      html  css  js  c++  java
  • 题解 P4402 [Cerc2007]robotic sort 机械排序

    区间翻转?第一反应Splay

    wtcl 不会FHQtreap

    观察题意,题目让我们依次输出第 (k) 大所在位置 (p_k) 并将 ([k,p_k]) 翻转。并且我们自始至终只关心位置。所以我们可以按照高度排序,从最小的依次找起。我们将最初始的序列元素依次编号 (1,2,3 cdots),问题就落在了如何在序列中查找第 (k) 号元素,带翻转操作。

    我们利用 Splay 的中序遍历维护序列, Splay 的旋转操作不会改变其中序序列。然后当我们查询第 (i) 号元素时,我们就将 (i) 转到根,此时根据平衡树的性质,它的左子树大小 (+1) 就是答案。翻转操作请参考照搬P3391 【模板】文艺平衡树

    总之,是一道不错的 Splay 练手题。并且你还可以多做几遍以提高熟练度(指多倍经验

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=1e5+10,INF=1e8+10;
    
    struct node
    {
    	int s[2],p,v;
    	int size,flag;
    
    	void init(int _v)
    	{
    		v=_v;
    		size=1; flag=0;
    	}
    } tree[N];
    
    int root,idx=0;
    
    int n;
    struct pak
    {
    	int x,no;
    } poi[N];
    
    bool cmp(pak a,pak b)
    {
    	if(a.x==b.x) return a.no<b.no;
    	else return a.x<b.x;
    }
    
    inline int ls(int x){return tree[x].s[0];}
    inline int rs(int x){return tree[x].s[1];}
    
    void push_up(int x)
    {
    	if(!x) return;
    	tree[x].size=tree[ls(x)].size+tree[rs(x)].size+1;
    }
    
    void push_down(int x)
    {
    	if(!tree[x].flag) return ;
    	swap(tree[x].s[0],tree[x].s[1]);
    	if(ls(x)) tree[ls(x)].flag^=1;
    	if(rs(x)) tree[rs(x)].flag^=1;
    	tree[x].flag=0;
    }
    
    int build(int l,int r)//严格对照中序遍历的方式建树
    {
    	int mid=l+r>>1;
    	int la=0,ra=0;
    	if(l<mid) la=build(l,mid-1);
    	int u=++idx;
    	tree[u].init(poi[mid].x);
    	if(r>mid) ra=build(mid+1,r);
    	tree[u].s[1]=ra; tree[u].s[0]=la;
    	tree[ls(u)].p=tree[rs(u)].p=u;
    	push_up(u);
    	return u;
    }
    
    void rotate(int x)
    {
    	int y=tree[x].p, z=tree[y].p;
    	int k=tree[y].s[1]==x;
    
    	tree[z].s[tree[z].s[1]==y]=x;
    	tree[x].p=z;
    
    	tree[y].s[k]=tree[x].s[k^1];
    	tree[tree[x].s[k^1]].p=y;
    
    	tree[x].s[k^1]=y; tree[y].p=x;
    	push_up(y), push_up(x);
    }
    
    void splay(int x,int k)
    {
    	while(tree[x].p!=k)
    	{
    		int y=tree[x].p,z=tree[y].p;
    		push_down(z); push_down(y); push_down(x);
    		if(z!=k)
    		{
    			if((tree[y].s[1]==x)^(tree[z].s[1]==y)) rotate(x);
    			else rotate(y);
    		}
    		rotate(x);
    	}
    	if(!k) root=x;
    }
    
    int get_k(int k)
    {
    	int u=root;
    	while(1)
    	{
    		push_down(u);
    		if(tree[tree[u].s[0]].size>=k) u=tree[u].s[0];
    		else if(tree[tree[u].s[0]].size+1==k) return u;
    		else k-=tree[tree[u].s[0]].size+1,u=tree[u].s[1];
    	}
    	return -1;
    }
    
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%d",&poi[i].x);
    		poi[i].no=i;
    	}
    	poi[0].x=-INF,poi[0].no=1;
    	poi[n+1].x=INF,poi[n+1].no=n+1;
    	sort(poi+1,poi+1+n,cmp);
    	root=build(0,n+1);//建树无所谓在哪里,我们只关心某棵子树的左子树
    	for(int i=1;i<n;i++)
    	{
    		splay(poi[i].no+1,0);//记得加上哨兵节点
    		int s=tree[tree[root].s[0]].size;//答案也要减去哨兵
    		printf("%d ",s);
    		int l=get_k(i),r=get_k(s+2);
    		splay(l,0); splay(r,l);
    		tree[tree[r].s[0]].flag^=1;
    	}
    	printf("%d",n);
    	return 0;
    }
    
    
  • 相关阅读:
    《RocketMQ源码系列》心跳机制
    《RocketMQ源码系列》broker是如何注册到nameserver的
    使用redis客户端工具RedisClient连接windows和linux下的redis并解决无法连接redis的问题
    windows下安装Linux
    redis客户端工具RedisClient的使用
    redis哨兵机制配置
    redis数据的两种持久化方式rdb和aof对比(二)
    redis数据的两种持久化方式rdb和aof对比(一)
    windows下的redis主从复制
    redis持久化配置:rdb和aof
  • 原文地址:https://www.cnblogs.com/IzayoiMiku/p/14589267.html
Copyright © 2011-2022 走看看