Box

\[Time Limit: 5000 ms \quad Memory Limit: 32768 kB
\]

题意

给出 \(n\) 个箱子的包含关系,每次两种操作。

操作 \(1\):把 \(x\) 的箱子及里面的箱子一块挪到 \(y\) 箱子里面去。

操作 \(2\):查询 \(x\) 箱子的最外层的箱子编号。

思路

对于每一大块箱子,可以看成树和子树的包含关系,那么可以根据树的 \(dfs\) 序,把一个箱子所包含的箱子拍成一个区间,这样就变成了很多颗树。为了方便后面的操作,对于每个箱子 \(x\) 所包含的区间,用标号 \(x\) 来表示开始的位置,\(x+n\) 来表示结束的位置。

使用 \(splay\) 维护每多颗树的 \(dfs\) 序。由于每棵树都有一个自己的 \(root\) 节点,我们让他们的 \(root\) 节点统一连向 \(0\) 这个虚点。

再来看一看操作,操作 \(1\) 可以变成删除操作以及区间移动操作。操作 \(2\) 就是普通的查询操作。

对于操作 \(1\),先找到 \(x\) 和 \(x+n\) 所在的位置,把 \(x\) \(splay\) 到其树的 \(root\) 上,把 \(x+n\) \(slay\) 到 \(x\) 的右子树上,那么我们要转移的树就是 \(x->x+n->x+n\)的所有左子树。 同样的,要查找非法条件,只要看 \(y\) 在不在这棵树里面就可以了。

最后只要把这棵树插到标号 \(y\) 的右边就可以了。可以先把 \(y\) \(splay\) 到其 \(root\) 上,然后找到 \(y\) 下标意义上的后继,然后插入就行了。

对于操作\(2\),只要把 \(x\) \(splay\) 到其树的 \(root\) 上,然后查询这棵树里面的最小标号是多少,也就找到了最外的箱子的标号。

/***************************************************************
> File Name : a.cpp
> Author : Jiaaaaaaaqi
> Created Time : Mon 14 Oct 2019 07:20:55 PM CST
***************************************************************/ #include <map>
#include <set>
#include <list>
#include <ctime>
#include <cmath>
#include <stack>
#include <queue>
#include <cfloat>
#include <string>
#include <vector>
#include <cstdio>
#include <bitset>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define lowbit(x) x & (-x)
#define mes(a, b) memset(a, b, sizeof a)
#define fi first
#define se second
#define pb push_back
#define pii pair<int, int> typedef unsigned long long int ull;
typedef long long int ll;
const int maxn = 1e5 + 10;
const int maxm = 1e5 + 10;
const ll mod = 1e9 + 7;
const ll INF = 1e18 + 100;
const int inf = 0x3f3f3f3f;
const double pi = acos(-1.0);
const double eps = 1e-8;
using namespace std; int n, m;
int cas, tol, T; vector<int> vv[maxn];
int root, tot;
int ch[maxn][2];
int sz[maxn], fa[maxn], val[maxn], cnt[maxn];
int a[maxn], id[maxn]; void init() {
tol = root = tot = 0;
for(int i=0; i<=n; i++) {
vv[i].clear();
}
} int newnode(int x, int f) {
mes(ch[++tot], 0);
val[tot] = x;
id[x] = tot;
fa[tot] = f;
sz[tot] = cnt[tot] = 1;
return tot;
} void pushdown(int x) { } void pushup(int x) {
if(x) {
sz[x] = cnt[x];
if(ch[x][0]) sz[x] += sz[ch[x][0]];
if(ch[x][1]) sz[x] += sz[ch[x][1]];
}
} int get(int x) {
return ch[fa[x]][1] == x;
} void rotate(int x) {
int f = fa[x], gf = fa[f], k = get(x), w = ch[x][k ^ 1];
pushdown(f), pushdown(x);
ch[f][k] = w, fa[w] = f;
ch[gf][get(f)] = x, fa[x] = gf;
ch[x][k ^ 1] = f, fa[f] = x;
pushup(f), pushup(x);
} void splay(int x, int goal = 0) {
int f, gf;
while (fa[x] != goal) {
f = fa[x], gf = fa[f];
pushdown(gf), pushdown(f), pushdown(x);
if (gf != goal) {
if(get(x) == get(f)) rotate(f);
else rotate(x);
}
rotate(x);
}
if (!goal) root = x;
} int build(int l, int r, int f) {
if(l > r) return 0;
int mid = l+r>>1;
int now = newnode(a[mid], f);
ch[now][0] = build(l, mid-1, now);
ch[now][1] = build(mid+1, r, now);
pushup(now);
return now;
} void dfs(int u) {
a[tol++] = u;
for(auto v : vv[u]) {
dfs(v);
}
a[tol++] = u+n;
} void Move(int a, int b) {
if(a == b) return ;
int x = id[a], y = id[a+n], z = id[b];
splay(x), splay(y, x);
while(z && z!=y) {
if(ch[y][0] == z) return ;
z = fa[z];
}
if(ch[x][0]==0 || ch[y][1]==0) {
fa[ch[x][0]+ch[y][1]] = 0;
} else {
int k = ch[y][1];
while(ch[k][0]) k = ch[k][0];
splay(k, y);
int cur = ch[y][1];
ch[cur][0] = ch[x][0];
fa[ch[x][0]] = cur;
fa[cur] = 0;
pushup(cur);
}
ch[x][0] = ch[y][1] = 0;
if(b == 0) return ;
z = id[b];
splay(z);
if(ch[z][1]) {
z = ch[z][1];
while(ch[z][0]) z = ch[z][0];
}
ch[z][0] = x;
fa[x] = z;
pushup(z);
} void Query(int x) {
x = id[x];
splay(x);
while(ch[x][0]) x = ch[x][0];
printf("%d\n", val[x]);
} int main() {
// freopen("in", "r", stdin);
int flag = 0;
while(~scanf("%d", &n)) {
if(flag) printf("\n");
flag = 1;
init();
for(int i=1; i<=n; i++) {
int x;
scanf("%d", &x);
vv[x].pb(i);
}
dfs(0);
tol--;
// for(int i=1; i<tol; i++) printf("%d%c", a[i], i==tol-1 ? '\n':' ');
int st = 1, c = 0;
for(int i=1; i<tol; i++) {
if(a[i] <= n) c++;
else c--;
if(c == 0) {
build(st, i, 0);
st = i+1;
}
}
// for(int i=1; i<=tot; i++) printf("val[%d] = %d\n", i, val[i]);
scanf("%d", &m);
char s[10];
while(m--) {
int x, y;
scanf("%s", s+1);
if(s[1] == 'M') {
scanf("%d%d", &x, &y);
Move(x, y);
} else {
scanf("%d", &x);
Query(x);
}
}
}
}

Box HDU - 2475 (Splay 维护森林)的更多相关文章

  1. HDU - 2475:Box(splay维护森林)

    There are N boxes on the ground, which are labeled by numbers from 1 to N. The boxes are magical, th ...

  2. hdu 2475 BOX (splay)

    版权声明:本文为博主原创文章,未经博主允许不得转载. hdu 2475 Splay树是一种神奇的东西... 题意: 有一些箱子,要么放在地上,要么放在某个箱子里面 . 现在有两种操作: (1) MOV ...

  3. HDU 2475 BOX 动态树 Link-Cut Tree

    Box Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) [Problem De ...

  4. hdu 3436 splay树+离散化*

    Queue-jumpers Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) To ...

  5. hdu 4453 splay

    Looploop Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total S ...

  6. 【BZOJ 3729】3729: Gty的游戏 (Splay维护dfs序+博弈)

    未经博主同意不得转载 3729: Gty的游戏 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 448  Solved: 150 Description ...

  7. HNOI2004宠物收养所(splay维护二叉搜索树模板题)

    描述 最近,阿Q开了一间宠物收养所.收养所提供两种服务:收养被主人遗弃的宠物和让新的主人领养这些宠物.每个领养者都希望领养到自己满意的宠物,阿Q根据领养者的要求通过他自己发明的一个特殊的公式,得出该领 ...

  8. BZOJ 3729 splay维护DFS序+博弈论

    思路: 这像是 阶梯Nim之类的东西 我们 直接把sg函数 设成mod(L+1)的 一棵子树 向下的奇数层上的石子xor起来 就是答案 有加点和改值的操作 就splay维护一下 //By Sirius ...

  9. BZOJ 1492 [NOI2007]货币兑换Cash (CDQ分治/splay 维护凸包)

    题目大意:太长了略 splay调了两天一直WA弃疗了 首先,我们可以猜一个贪心,如果买/卖,就一定都买/卖掉,否则不买/卖 反正货币的行情都是已知的,没有任何风险,所以肯定要选择最最最优的方案了 容易 ...

随机推荐

  1. centos 安装 oracle11r2

    因为要测试spark链接oracle,所以需要再服务器装oracle 1.下载oracle, 如果自己下载需要注册,比较麻烦,可以直接用如下命令下载 因为zip比较大,建议nohup 后台下载 noh ...

  2. Linux record

    1.设置ubuntu密码刚安装好的ubuntu系统,没有root密码,需要用户去手动设置的. sudo passwd root 输入2次密码即可. 2. Linux下is not in the sud ...

  3. Java中json使用与问题汇总

    一.JSON 解析类库 FastJson: 阿里巴巴开发的 JSON 库,性能十分优秀. 在maven项目的pom文件中以下依赖 <dependency> <groupId>c ...

  4. rem与em的使用和区别

    区别是:浏览器根据谁来转化成px值. 当使用rem单位,转换为像素大小取决于根元素的字体大小,即HTML元素的字体大小. 有一个比较普遍的误解,认为em单位是相对于父元素的字体大小.事实上,根据W3C ...

  5. springboot指定注解扫描范围

    springboot注解扫描范围是由@ComponentScan指定的;默认情况下为启动程序所在目录及其子包; 如果需要指定扫描路径,在启动程序中添加修改 @ComponentScan(basePac ...

  6. Spring Boot + Vue 前后端分离开发,权限管理的一点思路

    在传统的前后端不分的开发中,权限管理主要通过过滤器或者拦截器来进行(权限管理框架本身也是通过过滤器来实现功能),如果用户不具备某一个角色或者某一个权限,则无法访问某一个页面. 但是在前后端分离中,页面 ...

  7. instr函数的用法

    1.定义 instr函数返回要截取的字符串在源字符串中的位置 语法如下:  instr( string1, string2 [, start_position [, nth_appearance ] ...

  8. vue自学笔记

      做前端也做了一段时间了,为了高薪,不能一直做网页不是~~,所以从今天开始整理vue的笔记 内容都是从网上搜集整合并且自己实践过了的,需要注意的点,也在后面标注了“注”   当然了,如果有什么问题的 ...

  9. delphi 接口

    第四章          接口 前不久,有位搞软件的朋友给我出了个谜语.谜面是“相亲”,让我猜一软件术语.我大约想了一分钟,猜 出谜底是“面向对象”.我觉得挺有趣,灵机一动想了一个谜语回敬他.谜面是“ ...

  10. mpvue 小程序开发之 数据埋点统计

    mpvue 小程序开发之 数据埋点统计 在开发过程中,有数据统计的需求,需要获取小程序当前页面和来源页面的数据,以及页面的停留时间 在对小程序api进行了一番研究之后,发现获取这些数据其实并不难 当前 ...