@codeforces - 1083C@ Max Mex


@description@

给定一棵树 T,并给定一个 0~n-1 的排列 p,第 i 个结点上写着 p[i]。

有 q 次操作,共两类操作:
(1)给定 i, j,交换 p[i] 与 p[j]。
(2)对于 T 上所有简单路径,求路径上所有 p 组成的集合的 mex 的最大值。

Input
第一行包含一个整数 n (2≤n≤2⋅10^5),表示树上的结点数。
第二行包含 n 个整数,描述了排列 p (0≤pi<n)。
第三行包含 n-1 个整数 d1, d2, ..., dn (1≤di<i),di 描述了 i 的父亲。
第四行一个整数 q,表示询问数 (1≤q≤2⋅10^5)。
接下来 q 行,每行包含一个询问。首先给定一个 t 表示询问类型。
如果 t = 1,则再给定两个整数 i, j,表示交换 a[i], a[j]。
否则表示一组询问。

output
对于每个询问 2,输出一个整数表示答案。

Examples
Input
6
2 5 0 3 1 4
1 1 3 3 3
3
2
1 6 3
2
Output
3
2

Input
6
5 2 1 4 3 0
1 1 1 3 3
9
2
1 5 3
2
1 6 1
2
1 4 2
2
1 1 6
2
Output
3
2
4
4
2

@solution@

不难想到二分:在 0 到 n 中二分一个值 x,然后判断是否存在一条链的 mex >= x。
假如我们记 a[p[x]] = x,即根据权值反过来找点。则上面的判定条件等价于 a[0...x-1] 在同一条链上。

怎么判断 a[0...x-1] 是否在同一条链上?
不难想到使用增量法,即每一次尝试把 a[i] 与 a[0...i-1] 形成的最小链组成一条新链。
因为新最小链的端点一定在 a[i] 与 a[0...i-1] 形成的链的端点中产生,所以我们可以枚举,然后做一个点是否在路径上的查询。
至于怎么查询一个点在路径上,用 lca 以及 dfs 序即可实现,不再赘述。

这个算法慢在,我们每次只能一个一个点地加入。
假如我每次是将两个链合成一起,而不是点插入链呢?
合并两个链与上面大致类似,新的最小链的端点一定在旧的最小链端点中产生。枚举然后做点是否在路径上的查询。

既然支持合并,那线段树就可以做了嘛。
对于线段树上的每个结点 [l, r],存 a[l...r] 是否能形成一条链,以及链的两个端点。
修改就针对线段树的两个位置对应修改即可。
然后线段树上二分 O(nlogn),求 LCA 如果用倍增就多个 log。

@accepted code@

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef pair<int, int> pii;
#define mp make_pair
#define fi first
#define se second
const int MAXN = 200000;
int lg[2*MAXN + 5];
struct tree{
    struct edge{
        int to; edge *nxt;
    }edges[MAXN + 5], *adj[MAXN + 5], *ecnt;
    int tid[MAXN + 5], dep[MAXN + 5], siz[MAXN + 5], dcnt;
    int fir[MAXN + 5], dfn[2*MAXN + 5], st[20][2*MAXN + 5], dcnt1;
    tree() {ecnt = &edges[0], dcnt = dcnt1 = 0;}
    void addedge(int u, int v) {
        edge *p = (++ecnt);
        p->to = v, p->nxt = adj[u], adj[u] = p;
    }
    void dfs(int x, int f) {
        tid[x] = (++dcnt), dep[x] = dep[f] + 1, siz[x] = 1;
        dfn[++dcnt1] = x, fir[x] = dcnt1;
        for(edge *p=adj[x];p;p=p->nxt)
            dfs(p->to, x), dfn[++dcnt1] = x, siz[x] += siz[p->to];
    }
    void get_st() {
        for(int i=2;i<=dcnt1;i++) lg[i] = lg[i>>1] + 1;
        for(int i=1;i<=dcnt1;i++) st[0][i] = dfn[i];
        for(int j=1;j<20;j++) {
            int t = (1<<(j-1));
            for(int i=1;i+t<=dcnt1;i++)
                st[j][i] = (dep[st[j-1][i]] <= dep[st[j-1][i+t]]) ? st[j-1][i] : st[j-1][i+t];
        }
    }
    void build() {dfs(1, 0); get_st();}
    int lca(int u, int v) {
        int p = fir[u], q = fir[v];
        if( p > q ) swap(p, q);
        int l = lg[q - p + 1], k =(1<<l);
        return (dep[st[l][p]] <= dep[st[l][q-k+1]]) ? st[l][p] : st[l][q-k+1];
    }
    bool subtree(int x, int y) {return tid[x] <= tid[y] && tid[y] <= tid[x] + siz[x] - 1;}
    bool line(int u, int v, int x) {return subtree(v, x) && subtree(x, u);}
    bool line(int u, int v, int l, int x) {return line(u, l, x) || line(v, l, x);}
    bool check(int u, int v, int x, int y) {
        int l = lca(u, v);
        return line(u, v, l, x) && line(u, v, l, y);
    }
}T;
pii merge(pii a, pii b) {
    if( a.fi == 0 ) return b;
    if( b.fi == 0 ) return a;
    if( a.fi == -1 || b.fi == -1 ) return mp(-1, -1);
    if( T.check(a.fi, b.fi, a.se, b.se) ) return mp(a.fi, b.fi);
    if( T.check(a.fi, b.se, a.se, b.fi) ) return mp(a.fi, b.se);
    if( T.check(a.se, b.fi, a.fi, b.se) ) return mp(a.se, b.fi);
    if( T.check(a.se, b.se, a.fi, b.fi) ) return mp(a.se, b.se);
    if( T.check(a.fi, a.se, b.fi, b.se) ) return mp(a.fi, a.se);
    if( T.check(b.fi, b.se, a.fi, a.se) ) return mp(b.fi, b.se);
    return mp(-1, -1);
}
int p[MAXN + 5], a[MAXN + 5];
struct segtree{
    #define lch (x<<1)
    #define rch (x<<1|1)
    int le[4*MAXN + 5], ri[4*MAXN + 5];
    pii key[4*MAXN + 5];
    void pushup(int x) {key[x] = merge(key[lch], key[rch]);}
    void build(int x, int l, int r) {
        le[x] = l, ri[x] = r;
        if( l == r ) {
            key[x] = mp(a[l], a[l]);
            return ;
        }
        int mid = (l + r) >> 1;
        build(lch, l, mid), build(rch, mid + 1, r);
        pushup(x);
    }
    void update(int x, int p) {
        if( le[x] == ri[x] ) {
            key[x] = mp(a[p], a[p]);
            return ;
        }
        int mid = (le[x] + ri[x]) >> 1;
        if( p <= mid ) update(lch, p);
        else update(rch, p);
        pushup(x);
    }
    int query(int x, pii nw) {
        if( le[x] == ri[x] )
            return merge(nw, key[x]).fi != -1;
        pii p = merge(nw, key[lch]);
        int mid = (le[x] + ri[x]) >> 1;
        if( p.fi == -1 ) return query(lch, nw);
        else return query(rch, p) + (mid - le[x] + 1);
    }
    int query() {return query(1, mp(0, 0));}
}segT;
int n, q;
int main() {
    scanf("%d", &n);
    for(int i=1;i<=n;i++)
        scanf("%d", &p[i]), p[i]++, a[p[i]] = i;
    for(int i=2;i<=n;i++) {
        int d; scanf("%d", &d);
        T.addedge(d, i);
    }
    T.build();
    segT.build(1, 1, n);
    scanf("%d", &q);
    for(int i=1;i<=q;i++) {
        int op; scanf("%d", &op);
        if( op == 1 ) {
            int x, y; scanf("%d%d", &x, &y);
            swap(a[p[x]], a[p[y]]), swap(p[x], p[y]);
            segT.update(1, p[x]), segT.update(1, p[y]);
        }
        else printf("%d\n", segT.query());
    }
}
知识兔

@details@

写倍增 LCA 然后 T 了,一怒之下换了 ST 表的 LCA 过了这道题。

计算机