• [置顶] hdu 1890 伸展树区间翻转


    题意: 给你n个数,每次先输出第i大的数的位置(如果有多个,选下标小的那个),然后每次将第i个位置到第i大的数所在位置之间的数进行翻转。

    思路:输入的数组可能有多个相同的值,我们可以进行两次排序把数组的值变为1---n(表示第几大)。

    在建伸展树的时候我们可以顺便用pos[i]记录第i大的数的节点标号。

    对于第i次操作,我们用col[]数组记录翻转标记,每次先把第i大的节点pos[i]旋转到根,那么它的位置为i+左儿子的个数。然后左儿子打上翻转标记,最后删除根。

    注意:下放懒惰标记时只要交换左右儿子的节点标号就可以了,也正因为这个原因, 

    下放函数的位置记得要放在没有引用任何左右儿子信息之前, 这跟区间其它操作最大的区别。


    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    #define L ch[x][0]
    #define R ch[x][1]
    const int maxn = 100005;
    int pos[maxn]; //pos[i]表示第i大的数的节点的标号
    int n;
    struct node {
    	int a, id;
    	bool operator <(const node &t) const {
    		return id < t.id;
    	}
    }p[maxn];
    bool cmp(const node &a, const node &b) {
    	return a.a < b.a || (a.a == b.a && a.id < b.id);
    }
    struct splayTree {
    	int sz[maxn], ch[maxn][2], pre[maxn];
    	bool col[maxn];
    	int root, tot;
    	void down(int x) {
    		if(col[x]) {
    			col[L] ^= 1;
    			col[R] ^= 1;
    			swap(L, R);
    			col[x] = 0;
    		}
    	}
    	void up(int x) {
    		sz[x] = sz[L] + sz[R] + 1;
    	}
    	void rotate(int &x, int f) {
    		int y = pre[x], z = pre[y];
    		down(y); down(x);
    		ch[y][!f] = ch[x][f];
    		pre[ch[x][f]] = y;
    		pre[x] = pre[y];
    		if(pre[x]) ch[z][ch[z][1] == y] = x;
    		ch[x][f] = y;
    		pre[y] = x;
    		up(y);
    	}
    	void splay(int &x, int g) {
    		while(pre[x] != g) {
    			int y = pre[x], z = pre[y];
    			down(z); down(y); down(x);
    			//不是区间翻转的题,这里的down可以不写,因为rotate里面有down, 但区间翻转要先down在去旋转,因为左右儿子会改变
    			if(z == g) rotate(x, ch[y][0] == x);
    			else {
    				int f = (ch[z][0] == y);
    				ch[y][!f] == x ? rotate(y, f) : rotate(x, !f);
    				rotate(x, f);
    			}
    		}
    
    		up(x);
    		if(!g) root = x;
    	}
    	int find(int k) {
    		int x = root;
    		while(sz[L]+1 != k) {
    			down(x);
    			if(sz[L]>= k) x = L;
    			else {
    				k -= sz[L]+1;
    				x = R;
    			}
    		}
    		return x;
    	}
    	void rto(int k, int g) {
    		int x = root;
    		while(1) {
    			down(x);
    			if(sz[L]+1 == k) break;
    			if(sz[L]>= k) x = L;
    			else {
    				k -= sz[L]+1;
    				x = R;
    			}
    		}
    		splay(x, g);
    	}
    	void newNode(int &x, int m, int fa) {
    		x = ++tot;
    		pos[p[m].a] = x;
    		pre[x] = fa;
    		sz[x] = 1;
    		L = R = 0;
    		col[x] = 0;
    	}
    	void build(int &x, int l, int r, int fa) {
    		if(l > r) return;
    		int m = (l + r) >> 1;
    		newNode(x, m, fa);
    		build(L, l, m-1, x);
    		build(R, m+1, r, x);
    		up(x);
    	}
    	void init(int n) {
    		tot = 0;
    		int i;
    		  //数字可能相等,可以把数字预处理成1--n
    		for(i = 1; i <= n; i++) {
    			scanf("%d", &p[i].a);
    			p[i].id = i;
    		}
    		sort(p+1, p+n+1, cmp);
    		for(i = 1; i <= n; i++)
    			p[i].a = i;
    		sort(p+1, p+n+1);
    
    
    		build(root, 1, n, 0);
    	}
    	void print(int x) {
    	    down(x);
    	    printf("x: %d lson: %d rson: %d fa: %d lsz: %d rsz: %d
    ", x, L, R, pre[x], sz[L], sz[R]);
    	    if(L)print(L);
    	    if(R)print(R);
    	}
    	void debug() {
            printf("root = %d
    ", root);
            print(root);
    	}
    	void solve() {
    		for(int i = 1; i < n; i++) {
    			splay(pos[i], 0);	//把值为i的节点旋到根
    			int x = root;
    			printf("%d ", sz[L]+i);
    			   down(x); col[L] ^= 1; down(L); //根down,根的左儿子打翻转标记
    		    if(sz[L]) {    //有左儿子
    			    rto(sz[L], root); //把左儿子的最右边的点旋到根
    			    //删除根,根的左儿子代替根,新根的右儿子还是原根的右儿子,但父亲要修改
                    root = L;
                    ch[root][1] = R;
                    pre[root] = 0;
                    pre[R] = root;
    			}
    			else { //没有左儿子,直接把右儿子拉到根上来
    			    root = ch[root][1];
    			    pre[root] = 0;
    			}
                up(root);
    		}
    		printf("%d
    ", n);//最后只剩一个节点时一定是最后一个, 特判一下。
    	}
    }spt;
    int main() {
        int i;
    	while( ~scanf("%d", &n) && n) {
    		spt.init(n);
            spt.solve();
    	}
    	return 0;
    }
    


  • 相关阅读:
    Java I/O(二 使用)
    Java 基本I/O的学习总结(一 是什么)
    设计模式(一)
    浏览器输入一个网址(发生的过程)
    final关键字的4种用法
    JavaScript(4)——闭包与this对象以及window对象
    JavaScript(3)—— 正则表达式
    JavaScript(2)——对象属性、原型与原型链
    JavaScript(1)——变量、函数声明及作用域
    构建分布式配置中心阿波罗(Apollo)
  • 原文地址:https://www.cnblogs.com/suncoolcat/p/3299477.html
Copyright © 2020-2023  润新知