• 【bzoj5063】旅游 Splay


    题目描述

    给定一个1...n的序列,有m次操作,每次操作有6步:
    1、从序列开头(左端)取出A个数(此时序列剩下n-A个数)
    2、从序列开头取出B个数
    3、将第1步取出的A个数按原顺序放回序列开头
    4、从序列开头取出C个数
    5、将第2步取出的B个数逆序放回序列开头
    6、将第4步取出的C个数按原顺序放回序列开头
    你需要求出最终序列。

    输入

    第一行两个数n,m。接下来m行,每行三个数A,B,C。
    n,m<=100000

    输出

    输出一行n个数表示最终序列。

    样例输入

    10 2
    6 2 2
    5 3 6

    样例输出

    1 2 8 7 3 9 6 5 4 10


    题解

    Splay裸题

    题目中操作显然可以直接使用Splay解决

    然而本题卡常,直接模拟每次的6个步骤会T得很惨。。。

    考虑一下操作的实质:把第$A+1$个到第$A+B$个拿出来,再翻转一下放回到序列的第C个的后面。这个过程可以直接两次操作完成。常数减到直接模拟的1/3。

    注意最后输出时不能对于每一个都find一遍(这样Splay的均摊分析不成立,除非find后splay到根),最好的方法是dfs一遍Splay Tree,此时不要忘记pushdown。

    另外亲测数组版比结构体版慢了1倍左右= =

    #include <cstdio>
    #include <cctype>
    #include <algorithm>
    using namespace std;
    #define N 100010
    struct data
    {
    	int fa , c[2] , si , rev;
    }a[N];
    int root , ans[N] , tot;
    inline void rever(int x)
    {
    	swap(a[x].c[0] , a[x].c[1]) , a[x].rev ^= 1;
    }
    inline void pushup(int x)
    {
    	a[x].si = a[a[x].c[0]].si + a[a[x].c[1]].si + 1;
    }
    inline void pushdown(int x)
    {
    	if(a[x].rev) rever(a[x].c[0]) , rever(a[x].c[1]) , a[x].rev = 0;
    }
    inline void rotate(int &k , int x)
    {
    	int y = a[x].fa , z = a[y].fa , l = (a[y].c[1] == x) , r = l ^ 1;
    	if(y == k) k = x;
    	else a[z].c[a[z].c[1] == y] = x;
    	a[x].fa = z , a[y].fa = x , a[a[x].c[r]].fa = y , a[y].c[l] = a[x].c[r] , a[x].c[r] = y;
    	pushup(y) , pushup(x);
    }
    inline void splay(int &k , int x)
    {
    	int y , z;
    	while(x != k)
    	{
    		y = a[x].fa , z = a[y].fa;
    		if(y != k)
    		{
    			if((a[y].c[0] == x) ^ (a[z].c[0] == y)) rotate(k , x);
    			else rotate(k , y);
    		}
    		rotate(k , x);
    	}
    }
    int find(int k , int x)
    {
    	pushdown(k);
    	if(x <= a[a[k].c[0]].si) return find(a[k].c[0] , x);
    	else if(x > a[a[k].c[0]].si + 1) return find(a[k].c[1] , x - a[a[k].c[0]].si - 1);
    	else return k;
    }
    inline int split(int l , int r)
    {
    	int x = find(root , l) , y = find(root , r + 2);
    	splay(root , x) , splay(a[root].c[1] , y);
    	return a[a[root].c[1]].c[0];
    }
    int build(int l , int r)
    {
    	if(l > r) return 0;
    	int mid = (l + r) >> 1;
    	a[mid].c[0] = build(l , mid - 1) , a[a[mid].c[0]].fa = mid;
    	a[mid].c[1] = build(mid + 1 , r) , a[a[mid].c[1]].fa = mid;
    	pushup(mid);
    	return mid;
    }
    void dfs(int x)
    {
    	if(!x) return;
    	pushdown(x) , dfs(a[x].c[0]) , ans[++tot] = x , dfs(a[x].c[1]);
    }
    inline char nc()
    {
    	static char buf[100000] , *p1 , *p2;
    	return p1 == p2 && (p2 = (p1 = buf) + fread(buf , 1 , 100000 , stdin) , p1 == p2) ? EOF : *p1 ++ ;
    }
    inline int read()
    {
    	int ret = 0; char ch = nc();
    	while(!isdigit(ch)) ch = nc();
    	while(isdigit(ch)) ret = ((ret + (ret << 2)) << 1) + (ch ^ '0') , ch = nc();
    	return ret;
    }
    int main()
    {
    	int n = read() , m = read() , i , A , B , C , x;
    	root = build(1 , n + 2);
    	for(i = 1 ; i <= m ; i ++ )
    	{
    		A = read() , B = read() , C = read();
    		x = split(A + 1 , A + B) , a[a[x].fa].c[0] = 0 , pushup(a[x].fa) , pushup(root) , rever(x);
    		split(C + 1 , C) , a[x].fa = a[root].c[1] , a[a[x].fa].c[0] = x , pushup(a[x].fa) , pushup(root);
    	}
    	dfs(root);
    	for(i = 2 ; i <= n + 1 ; i ++ ) printf("%d " , ans[i] - 1);
    	return 0;
    }
    

     

  • 相关阅读:
    [LeetCode] 159. Longest Substring with At Most Two Distinct Characters 最多有两个不同字符的最长子串
    [LeetCode] 76. Minimum Window Substring 最小窗口子串
    window.scrollTo和window.scrollBy
    background-clip与background-origin
    page-break-before和page-break-after
    CSS counter计数器(content目录序号自动递增)详解
    移动端网页巧用 margin和padding 的百分比实现自适应
    监听屏幕旋转事件window. onorientationchange
    apple-touch-startup-image 制作iphone web应用程序的启动画面
    当把链接保存到手机桌面。设置图标 只在safari浏览器中有用
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/7715591.html
Copyright © 2020-2023  润新知