@description@

给定一棵 n 个点的树,每个点的儿子是有序的。

现给定 m 次操作,每次操作是下列三种中的一种:

(1)给定 u, v,询问 u, v 之间的距离。

(2)给定 v, h,断开 v 到父亲的边,将 v 这棵子树加入到它的第 h 个祖先的最后一个儿子。

(3)给定 k,询问在当前这棵树上 dfs 后得到 dfs 序中,最后一个深度为 k 的点的编号。

Input

第一行包含两个整数 n, m (2 ≤ n ≤ 10^5; 1 ≤ m ≤ 10^5),表示点数与询问数。

接下来 n 行每行一个 li,表示 i 号结点的儿子个数。紧接着输入 li 个整数,第 j 个整数表示 i 的第 j 个儿子编号(再次强调儿子是有序的)。

接下来 m 行每行形如 "1 v u", "2 v h", 或 "3 k"。第一个数描述操作种类,接下来描述了这个操作的参数。

保证操作合法。

Output

对于 1, 3 询问,输出其对应的答案。

Examples

Input1

4 9

1 2

1 3

1 4

0

1 1 4

2 4 2

1 3 4

3 1

3 2

2 3 2

1 1 2

3 1

3 2

Output1

3

2

2

4

1

3

4

Input2

2 2

1 2

0

1 2 1

3 1

Output2

1

2

@solution@

看到连边删边很自然地想到 lct,然而 lct 并没有办法很好地维护询问 3。。。

考虑对于连边删边的另一种处理方法:使用平衡树维护括号序,剪切某棵子树对应的序列与拼接某棵子树对应的序列即可。

然而它要求两点距离?那我们类比一下括号序,使用平衡树维护一下欧拉序即可(就是可以把 lca 问题转为 rmq 问题的那个东东)。

对于询问 1,我们提取出 u, v 之间的区间,查询里面 dep 的最小值 x,用 dep[u] + dep[v] - 2*x 即可。

对于询问 3,我们可以发现欧拉序相邻位置的深度是连续的。所以我们可以存储 dep 的最小值与 dep 的最大值来判断一棵子树内是否含有某个特定 dep 的值。

对于操作 2,我们首先要定位 x 的第 h 个祖先。可以提取出以 x 为结尾的序列前缀,在这个前缀上找最后一个深度为 dep[x]-h 的点——其实就是询问 3。然后剪切,拼接,把 x 的子树内所有点的深度减去一个量(打 tag 就好了)。

总复杂度就是 splay 的复杂度 O(nlogn)。

@accepted code@

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int MAXN = 200000;
struct Splay{
struct node{
node *fa, *ch[2];
int num, key, mx, mn, tg;
}pl[2*MAXN + 5], *root, *NIL;
Splay() {
root = NIL = &pl[0];
NIL->ch[0] = NIL->ch[1] = NIL->fa = NIL;
NIL->mx = -MAXN, NIL->mn = MAXN, NIL->tg = 0;
}
void set_child(node *x, node *y, int d) {
if( x != NIL ) x->ch[d] = y;
if( y != NIL ) y->fa = x;
}
void pushdown(node *x) {
if( x->tg ) {
if( x->ch[0] != NIL )
x->ch[0]->key += x->tg, x->ch[0]->mx += x->tg, x->ch[0]->mn += x->tg, x->ch[0]->tg += x->tg;
if( x->ch[1] != NIL )
x->ch[1]->key += x->tg, x->ch[1]->mx += x->tg, x->ch[1]->mn += x->tg, x->ch[1]->tg += x->tg;
x->tg = 0;
}
}
void pushup(node *x) {
x->mn = min(x->key, min(x->ch[0]->mn, x->ch[1]->mn));
x->mx = max(x->key, max(x->ch[0]->mx, x->ch[1]->mx));
}
void rotate(node *x) {
node *y = x->fa; int d = (y->ch[1] == x);
pushdown(y), pushdown(x);
if( y->fa != NIL ) set_child(y->fa, x, y->fa->ch[1] == y);
else x->fa = y->fa;
set_child(y, x->ch[!d], d);
set_child(x, y, !d);
if( y == root ) root = x;
pushup(y);
}
void splay(node *x, node *rt) {
pushdown(x);
while( x->fa != rt ) {
node *y = x->fa;
if( y->fa == rt )
rotate(x);
else {
if( (y->fa->ch[1] == y) == (y->ch[1] == x) )
rotate(y);
else rotate(x);
rotate(x);
}
}
pushup(x);
}
void debug(node *x) {
if( x == NIL ) return ;
pushdown(x);
debug(x->ch[0]);
printf("%d : %d %d %d | %d %d\n", x-pl, x->ch[0]-pl, x->ch[1]-pl, x->fa-pl, x->num, x->key);
debug(x->ch[1]);
}
int dist(node *x, node *y) {
if( x == y ) return 0;
splay(x, NIL), splay(y, root);
if( x->ch[0] == y ) {
int d = min(min(x->key, y->key), x->ch[0]->ch[1]->mn);
return x->key + y->key - 2*d;
}
else {
int d = min(min(x->key, y->key), x->ch[1]->ch[0]->mn);
return x->key + y->key - 2*d;
}
}
node *query(node *x, int d) {
pushdown(x);
if( x->ch[1]->mn <= d && d <= x->ch[1]->mx )
return query(x->ch[1], d);
else if( x->key == d ) {
splay(x, NIL);
return x;
}
else return query(x->ch[0], d);
}
node *prev(node *x, node *rt) {
splay(x, rt);
node *ret = x->ch[0];
while( ret->ch[1] != NIL )
ret = ret->ch[1];
splay(ret, rt);
return ret;
}
node *next(node *x, node *rt) {
splay(x, rt);
node *ret = x->ch[1];
while( ret->ch[0] != NIL )
ret = ret->ch[0];
splay(ret, rt);
return ret;
}
}T;
Splay::node *fir[MAXN + 5], *bac[MAXN + 5];
int n, m, root;
vector<int>G[MAXN + 5];
void addedge(int u, int v) {
G[u].push_back(v);
}
int dep[MAXN + 5], dfn[2*MAXN + 5], dcnt;
void newnode(Splay::node *nw, int x, int k) {
nw->ch[0] = nw->ch[1] = nw->fa = T.NIL;
nw->num = x, nw->key = nw->mn = nw->mx = k, nw->tg = 0;
}
void dfs(int x) {
dfn[++dcnt] = x, fir[x] = bac[x] = &T.pl[dcnt], newnode(&T.pl[dcnt], x, dep[x]);
for(int i=0;i<G[x].size();i++) {
int p = G[x][i];
dep[p] = dep[x] + 1, dfs(p);
dfn[++dcnt] = x, bac[x] = &T.pl[dcnt], newnode(&T.pl[dcnt], x, dep[x]);
}
}
Splay::node *build(int l, int r) {
if( l > r ) return T.NIL;
int mid = (l + r) >> 1;
Splay::node *p = &T.pl[mid];
p->ch[0] = build(l, mid - 1);
if( p->ch[0] != T.NIL ) p->ch[0]->fa = p;
p->ch[1] = build(mid + 1, r);
if( p->ch[1] != T.NIL ) p->ch[1]->fa = p;
T.pushup(p);
return p;
}
bool tag[MAXN + 5];
int main() {
scanf("%d%d", &n, &m);
for(int i=1;i<=n;i++) {
int l; scanf("%d", &l);
for(int j=1;j<=l;j++) {
int x; scanf("%d", &x);
addedge(i, x); tag[x] = true;
}
}
for(int i=1;i<=n;i++)
if( !tag[i] ) root = i;
newnode(&T.pl[++dcnt], 0, -1), dfs(root), newnode(&T.pl[++dcnt], 0, -1);
T.root = build(1, dcnt);
for(int i=1;i<=m;i++) {
int op; scanf("%d", &op);
if( op == 1 ) {
int v, u; scanf("%d%d", &v, &u);
printf("%d\n", T.dist(fir[v], fir[u]));
}
else if( op == 2 ) {
int v, h; scanf("%d%d", &v, &h);
T.splay(fir[v], T.NIL); Splay::node *u = T.query(fir[v]->ch[0], fir[v]->key-h);
Splay::node *l = T.prev(fir[v], T.NIL), *r = T.next(bac[v], T.root);
if( bac[l->num] == r ) bac[l->num] = l;
T.set_child(l, r->ch[1], 1);
Splay::node *p = r->ch[0];
newnode(r, u->num, u->key), r->ch[0] = p;
r->tg += 1-h, T.pushdown(r), T.pushup(r);
T.next(bac[u->num], T.NIL), T.splay(bac[u->num], T.root);
bac[u->num] = r;
T.set_child(T.root->ch[0], r, 1);
}
else if( op == 3 ) {
int k; scanf("%d", &k);
printf("%d\n", T.query(T.root, k)->num);
}
}
}

@details@

一开始建树的时候没有 pushup,陷入了奇怪的 RE。。。

之后发现,用指针邻接表的形式存储会改变儿子之间的顺序,于是改成了 vector。。。

然后就是注意 splay 查找的复杂度是均摊在 splay 操作里的,所以查找到某一个值就赶紧把它 splay 上去,不然可能会 T。

@codeforces - 414E@ Mashmokh's Designed Problem的更多相关文章

  1. [CF414E]Mashmokh's Designed Problem

    题意:给一棵树,有三个操作:①询问两点$(x,y)$之间的距离②把$x$和原来的父亲断开并连到它的$h$级祖先,作为新父亲最右的儿子③询问与根节点距离为$k$的点中最右的点是哪个点 用出栈入栈序$s_ ...

  2. codeforces 414D Mashmokh and Water Tanks

    codeforces 414D Mashmokh and Water Tanks 题意 题解 \(a_i\):第 \(i\) 层的结点个数. \(b_i\):第 \(i\) 层初始有水的结点个数. 如 ...

  3. 【codeforces 442B】 Andrey and Problem

    http://codeforces.com/problemset/problem/442/B (题目链接) 题意 n个人,每个人有p[i]的概率出一道题.问如何选择其中s个人使得这些人正好只出1道题的 ...

  4. Codeforces Gym 100015A Another Rock-Paper-Scissors Problem 找规律

    Another Rock-Paper-Scissors Problem 题目连接: http://codeforces.com/gym/100015/attachments Description S ...

  5. 屏蔽Codeforces做题时的Problem tags提示

    当在Codeforces上做题的时,有时会无意撇到右侧的Problem tags边栏,但是原本并不希望能够看到它. 能否把它屏蔽了呢?答案是显然的,我们只需要加一段很短的CSS即可. span.tag ...

  6. Codeforces 414B Mashmokh and ACM

    http://codeforces.com/problemset/problem/414/B 题目大意: 题意:一个序列B1,B2...Bl如果是好的,必须满足Bi | Bi + 1(a | b 代表 ...

  7. codeforces#253 D - Andrey and Problem里的数学知识

    这道题是这种,给主人公一堆事件的成功概率,他仅仅想恰好成功一件. 于是,问题来了,他要选择哪些事件去做,才干使他的想法实现的概率最大. 我的第一个想法是枚举,枚举的话我想到用dfs,但是认为太麻烦. ...

  8. Codeforces Round #243 (Div. 2) Problem B - Sereja and Mirroring 解读

    http://codeforces.com/contest/426/problem/B 对称标题的意思大概是.应当指出的,当线数为奇数时,答案是线路本身的数 #include<iostream& ...

  9. Codeforces 803 G. Periodic RMQ Problem

    题目链接:http://codeforces.com/problemset/problem/803/G 大致就是线段树动态开节点. 然后考虑到如果一个点还没有出现过,那么这个点显然未被修改,就将这个点 ...

随机推荐

  1. java窗体swing使用jlabel显示图片

    Icon icon = new ImageIcon("src\\resource\\" + jTFimgName.getText()); jLabColor.setIcon(ico ...

  2. springmvc 使用poi解析excel并通过hibernate连续插入多条数据 实际数据库只能保存最后一条

    有一个原始数据的excel表 用poi解析之后通过hibernate插数据库 结果 后来发现,有人说 果断尝试 问题解决 但是这好像并不是真正解决问题,只是解决了一个现象 因为有人说 https:// ...

  3. java-多线程的练习----妖,等待唤醒,代码重构,lock到condition

    1 需求 资源有姓名和性别. 两个线程,    一个负责给姓名和性别赋值,    一个负责获取姓名和性别的值. 要求1,运行一下,解决程序的 "妖"的问题. 要求2,实现正确数据的 ...

  4. java-静态-单例-继承

    概要图 一.静态 1.1 静态方法 创建对象就是为了产生实例,并进行数据的封装. 而调用功能时,确没有用到这些对象中封装的数据. 该对象的创建有意义吗?虽然可以编译并运行,但是在堆内存中空间较为浪费. ...

  5. 洛谷P2426 删数 [2017年4月计划 动态规划12]

    P2426 删数 题目描述 有N个不同的正整数数x1, x2, ... xN 排成一排,我们可以从左边或右边去掉连续的i(1≤i≤n)个数(只能从两边删除数),剩下N-i个数,再把剩下的数按以上操作处 ...

  6. 解决IE6、IE7、Firefox兼容最简单的CSS Hack

    写三句代码来控制一个属性,区别Firefox,IE7,IE6: background:orange; *background:green !important; *background:blue;   ...

  7. SpringMVC代码复制版

    Lib目录 Java目录 HelloController文件代码 import org.springframework.web.servlet.ModelAndView; import org.spr ...

  8. gitlab 添加本地项目

    1.安装git    https://git-scm.com/downloads 2.新建工程 3.创建密钥 a.桌面右键 b.cd ~/.ssh/ 如果提示 “ No such file or di ...

  9. yum源配置及详解

      红帽系列中,进行软件安装可以有三种方法,编译安装,rpm包安装,和yum源安装.其中yum方法安装最简单,因为它可以自动解决软件包之间的依赖关系... 一.常用yum源 yum源可以来源于多种文件 ...

  10. fedora下eclipse安装tomcat插件

    首先下载tomcat插件: http://www.eclipsetotale.com/tomcatPlugin.html,下载最新的3.3版本: 由于我的eclipse是通过yum自动安装的,因此ec ...