题意: 给你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\n", x, L, R, pre[x], sz[L], sz[R]);
if(L)print(L);
if(R)print(R);
}
void debug() {
printf("root = %d\n", 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", n);//最后只剩一个节点时一定是最后一个, 特判一下。
}
}spt;
int main() {
int i;
while( ~scanf("%d", &n) && n) {
spt.init(n);
spt.solve();
}
return 0;
}

[置顶] hdu 1890 伸展树区间翻转的更多相关文章

  1. hdu 5919 主席树(区间不同数的个数 + 区间第k大)

    Sequence II Time Limit: 9000/4500 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Tot ...

  2. [置顶] hdu 4699 2个栈维护 or 伸展树

    hdu 4699  Editor 题意:对一个数列进行操作,光标位置后面插入一个权值为x的数,删除光标前的那个数,光标左移一位,光标右移一位,求到k位置的最大的前缀和.. 注意这里的k是在光标之前的, ...

  3. HDU 3911 线段树区间合并、异或取反操作

    题目:http://acm.hdu.edu.cn/showproblem.php?pid=3911 线段树区间合并的题目,解释一下代码中声明数组的作用: m1是区间内连续1的最长长度,m0是区间内连续 ...

  4. hdu 1890 splay树

    Robotic Sort Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Tot ...

  5. HDU 1698 线段树 区间更新求和

    一开始这条链子全都是1 #include<stdio.h> #include<string.h> #include<algorithm> #include<m ...

  6. E - Just a Hook HDU - 1698 线段树区间修改区间和模版题

    题意  给出一段初始化全为1的区间  后面可以一段一段更改成 1 或 2 或3 问最后整段区间的和是多少 思路:标准线段树区间和模版题 #include<cstdio> #include& ...

  7. HDU 4348 主席树区间更新

    To the moon Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total ...

  8. hdu 1698 线段树 区间更新 区间求和

    Just a Hook Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  9. hdu 3308(线段树区间合并)

    LCIS Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submis ...

随机推荐

  1. c - 向一个排序好的数组插入一个数,插入后数组依然是排序好的

    概述 因为这里只是演示插入1个数,这里我不用malloc再重新分配,而是将原先数组的腾出一个占位符. 完整代码如下: #include <stdio.h> #define LEN 6 // ...

  2. JavaScript--Date函数

    1. Date函数 var now = new Date(); 获取当前日期对象 now对象->Date.prototype->Object.prototype 将一个字符串转换为Date ...

  3. 移动端web开发调试

    手机上安装chrome, 连接上usb允许调试,打开电脑的chrome,输入chrome://inspect 点击电脑页面的inspect即可,这时操作手机和电脑能达到同步显示. android4.4 ...

  4. macbook Android开发环境搭建,真机调试

    买了一台MacBook,本以为可以鼓捣一下iOS开发之类的,可惜导师要我做Android开发.无奈开始了在MacBook上开发Android的工作. 从开始配置环境到应用成功在真机上运行,也是曲曲折折 ...

  5. javascript获得给定日期的前一天的日期

    /** * 获得当前日期的前一天 */ function getYestoday(date){ var yesterday_milliseconds=date.getTime()-1000*60*60 ...

  6. thinkphp 内置函数详解

    D() 加载Model类M() 加载Model类 A() 加载Action类L() 获取语言定义C() 获取配置值    用法就是   C("这里填写在配置文件里数组的下标")S( ...

  7. SQL2008数据库连接服务器为主机名时连接成功,服务器为Ip地址时链接失败

    如图:

  8. 教你怎样在ppt2010抠图的小技巧|用ppt2010抠图的方法

    我们经常在ppt2010里做幻灯片时会碰到插入的图片并不是我们想要的情况,有的图片只是想要其中的一个部分.我们用“裁剪”功能也是达不到自己想要的效果.有的人会说PS抠图啊,但是比较繁琐,不易懂,不好上 ...

  9. c语言Winpcap编程构造并接收解析arp包

    /* 程序功能: 1.构造arp包,并发送.程序参数顺序:源IP.目的IP.mac地址.flag 2.获取网络中的ARP数据包,解析数据包的内容.程序参数:日志文件名 winpacp中文技术文档(基本 ...

  10. MySQL的索引

    MySQL索引 索引:是一种特殊的文件(InnoDB 数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针.其可以加快数据读操作,但会使数据写操作变慢:应该构建在被用作查询条 ...