@codeforces - [email protected] Mashmokh's Designed Problem

目录

  • @[email protected]
  • @[email protected]
  • @accepted [email protected]
  • @[email protected]

@[email protected]

给定一棵 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

@[email protected]

看到连边删边很自然地想到 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 [email protected]

#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);
        }
    }
}

@[email protected]

一开始建树的时候没有 pushup,陷入了奇怪的 RE。。。
之后发现,用指针邻接表的形式存储会改变儿子之间的顺序,于是改成了 vector。。。

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

@codeforces - [email protected] Mashmokh's Designed Problem

原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11332896.html

时间: 2024-10-09 01:29:37

@codeforces - [email protected] Mashmokh's Designed Problem的相关文章

Mashmokh&#39;s Designed Problem 01生成树

题意 给一张无向连通图, 每条边可能是黑边或白边, 问是否存在一种生成树构造使得树上黑边数量 = 白边数量 ? $1\le n,m\le 100000$ . 分析 我们可以把 黑边 当做 权值为1的边 , 白边 当做 权值为0的边 . 这样如果存在一种生成树构造, 使得树上黑边数量 = 白边数量 , 意味着 $n$ 为奇数, 且生成树的边权之和为 $\frac{n-1}{2}$ . 问题转化为: 对于一张边权为 $0$ 或 $1$ 的无向连通图, 是否有一种生成树, 其边权之和恰好等于 $m$

[CF414E]Mashmokh&#39;s Designed Problem

题意:给一棵树,有三个操作:①询问两点$(x,y)$之间的距离②把$x$和原来的父亲断开并连到它的$h$级祖先,作为新父亲最右的儿子③询问与根节点距离为$k$的点中最右的点是哪个点 用出栈入栈序$s_{1\cdots 2n}$来维护整棵树,入栈记$1$出栈记$-1$,那么一个节点$x$的深度就是$\sum\limits_{i=1}^{in_x}s_x$ 每个平衡树节点记1.这个节点是出栈还是入栈2.子树和3.最大前缀和4.最小前缀和,那么我们就可以在平衡树上二分找到最右的深度为$d$的节点(注意

@codeforces - [email&#160;protected] T-Shirts

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 有 n 件 T-shirt,第 i 件 T-shirt 有一个 ci 和 qi,分别表示费用与质量. 同时有 k 个顾客,第 j 个顾客准备了 bj 的金钱去购买 T-shirt. 每个顾客的购买策略是相同的: 他会买他的资金范围内 q 值最大的一件,如果有多个选 c 最小的一件,每种

@codeforces - [email&#160;protected] Oleg and chess

目录 @description - [email protected] @[email protected] @part - [email protected] @part - [email protected] @part - [email protected] @part - [email protected] @accepted [email protected] @[email protected] @description - [email protected] 给定一个 n*n 的棋

@codeforces - [email&#160;protected] Lucky Tickets

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 已知一个数(允许前导零)有 n 位(n 为偶数),并知道组成这个数的数字集合(并不一定要把集合内的数用完).求有多少种可能,使得这个数前半部分的数位和等于后半部分的数位和. 模 998244353. input 第一行两个整数:n k.表示这个数的位数以及组成这个数的数字集合大小.2

@codeforces - [email&#160;protected] Bandit Blues

目录 @[email protected] @[email protected] @part - [email protected] @part - [email protected] @accepted [email protected] @[email protected] @[email protected] 求有多少个长度为 n 的排列,从左往右遍历有 a 个数比之前遍历的所有数都大,从右往左遍历有 b 个数比之前遍历的所有数都大. 模 998244323. input 一行三个整数 n

@codeforces - [email&#160;protected] Vus the Cossack and a Field

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一个 n*m 的 01 矩阵,通过这个矩阵生成一个无穷矩阵,具体操作如下: (1)将这个矩阵写在左上角. (2)将这个矩阵每位取反写在右上角. (3)将这个矩阵每位取反写在左下角. (4)将这个矩阵写在右下角. (5)将得到的矩阵再作为初始矩阵,重复这些操作. 比如对于初始矩阵:

@codeforces - [email&#160;protected] Big Problems for Organizers

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] n 个点连成一棵树,经过每条边需要花费 1 个单位时间. 现给出 m 次询问,每次询问给出两个点,需要求所有点同时出发,最终所有点到达这两个点之一的最小花费时间. input 第一行包含一个整数 n (2?≤?n?≤?100000) ,表示点数. 接下来 n-1 行每行两个 1~n 的

@codeforces - [email&#160;protected] Strongly Connected Tournament

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] n 个选手参加了一场竞赛,这场竞赛的规则如下: 1.一开始,所有选手两两之间独立进行比赛(没有平局). 2.主办方将胜者向败者连边形成 n 个点的竞赛图. 3.主办方对这个竞赛图进行强连通分量缩点. 4.每一个强连通分量内部的选手重复步骤 1~3,直到每一个强连通分量内只剩一个选手.