@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 过了这道题。