【原题】
【题目翻译】
给定一棵有 n 个点的树,每个节点有点权。所有的点权构成了一个 0-n−1 的排列。有 q 次操作,每次操作 1 为交换两个点的点权,操作 2 为查询Mex(l) 值最大的 Mex(l) 值,其中 l 是树上的一条路径。定义一条路径 l 的 Mex 值 Mex(l) 为这条路径上最小的没有出现过的自然数
【输入格式】
第一行是一个整数 n 代表点数
下面一行是 n 个数字,代表每个点的点权,保证是一个 0-n−1 的排列
下面一行有 n - 1 个数字,第 i 个数字代表钦定 1 为根时第 i + 1个点的父节点
下面一行是一个整数 q,代表操作个数
下面 q 行,每行一组操作。
如果该行第一个数字为1,则后面有两个数字 x,y,代表交换 x,y 的点权
否则该行有且仅有一个数字 2,代表一次查询
S a m p l e I n p u t Sample~~Input Sample 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
S a m p l e O u t p u t Sample~~Output Sample Output
3
2
4
4
2
【题意分析】
部分参考自ww3113306
题目翻译成人话:两种操作:交换点权/查询整棵树中的所有路径上最小的未出现过的自然数
交换点权好处理,链上最小的未出现过的自然数怎么做呢?
如果x是答案,那么链上0~x-1都必须出现
于是用线段树节点的左右值域表示是否存在这样的路径,左儿子右儿子存树上dfs序编号(如果有)
因为权值唯一,可以在dfs的时候保存数据以实现编号<=>权值互化
考虑合并左右儿子 [ L , m i d ] [L,mid] [L,mid]和 [ m i d + 1 , R ] [mid+1,R] [mid+1,R](注意这边写出的是值,不是编号,编号存在线段树里)
四个值就有四个节点,我们从四个点中选两个,如果另外两个在这两个构成的路径上,不就大功告成了?
问题转化为:判断 x x x是否在 ( u , v ) (u,v) (u,v)组成的树上路径上
令 y = L C A ( u , v ) y=LCA(u,v) y=LCA(u,v)
画图理解:必须满足 L C A ( u , x ) = x LCA(u,x)=x LCA(u,x)=x或者 L C A ( v , x ) = x LCA(v,x)=x LCA(v,x)=x,并且 L C A ( x , y ) LCA(x,y) LCA(x,y)一定等于 y y y
显然本题要用到大量LCA,ST显然是最好的,但这玩意很容易写挂导致调很久
实测此题树剖求LCA跑得飞快甚至比ST快,倍增就可能跑不过了
查询操作可以在外面二分,但这样会t
我们可以在线段树上直接二分,这样就去掉了一个log,可以通过此题。
细节比较多,线段树内部我选择了pair
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cmath>
#include <algorithm>
#define MAXN 400100
#define mp make_pair
#define rep(x,a,b) for (register int x = a; x <= b; x++)
#define cross(x,a) for (register int x = head[a]; x; x = edge[x].next)
using namespace std;
typedef pair <int, int> pii;
struct fls {
int to, next;
}edge[MAXN << 1];
int head[MAXN << 1], v[MAXN], s[MAXN], id[MAXN], rk[MAXN], ST[MAXN][21];
int depth[MAXN], q[20], size[MAXN], son[MAXN], top[MAXN], father[MAXN], cnt, dfn, n;
pii tree[MAXN << 2], o;
inline void connect (int u, int v) {edge[++cnt].to = v, edge[cnt].next = head[u], head[u] = cnt;}
inline int f (int x, int y) {return depth[x] > depth[y] ? y : x;}
inline int read () {
register int s = 0, w = 1;
register char ch = getchar ();
while (! isdigit (ch)) {if (ch == '-') w = -1; ch = getchar ();}
while (isdigit (ch)) {s = (s << 3) + (s << 1) + (ch ^ 48); ch = getchar ();}
return s * w;
}
void DFS1 (int now, int fa, int d) {
father[now] = fa; depth[now] = d; size[now] = 1;
int maxson = -1;
cross (i, now) {
int v = edge[i].to;
if (v == fa)continue;
DFS1 (v, now, d+1);
size[now] += size[v];
if (size[v] > maxson) {
maxson = size[v];
son[now] = v;
}
}
}
void DFS2 (int now, int top_heavy) {
top[now] = top_heavy;
if (! son[now])return;
DFS2 (son[now], top_heavy);
cross (i, now) {
int v = edge[i].to;
if (v != father[now] && v != son[now])DFS2 (v,v);
}
}
inline int LCA (int x, int y) {
while (top[x] != top[y])
(depth[top[x]] >= depth[top[y]])
? x = father[top[x]]
: y = father[top[y]];
return (depth[x] < depth[y]) ? x : y;
}
namespace segtree {
inline bool islegal (int x, int u, int v) {
int tmp = LCA (u, v);
return (((LCA (x, u) == x) || (LCA (x, v) == x)) && (LCA (tmp, x) == tmp));
}
inline pii merge (pii L, pii R) {
if (! L.first || ! R.first) return mp (0, 0);
q[1] = L.first, q[2] = L.second, q[3] = R.first, q[4] = R.second;
rep (i, 1, 4) rep (j, i + 1, 4) {
if (q[i] == q[j]) continue; bool flag = 0;
rep (k, 1, 4) if (i != k && j != k && ! islegal (q[k], q[i], q[j])) flag = 1;
if (! flag) return mp (q[i], q[j]);
}
return mp (0, 0);
}
void build (int now, int L, int R) {
if (L == R) {
tree[now].first = tree[now].second = s[L];
return;
}
int mid = L + R >> 1;
build (now << 1, L, mid);
build (now << 1 | 1, mid + 1, R);
tree[now] = merge (tree[now << 1], tree[now << 1 | 1]);
}
void update (int now, int L, int R, int k) {
if (L == R) {
tree[now].first = tree[now].second = k;
return;
}
int mid = L + R >> 1;
if (v[k] <= mid) update (now << 1, L, mid, k);
else update (now << 1 | 1, mid + 1, R, k);
tree[now] = merge (tree[now << 1], tree[now << 1 | 1]);
}
int query (int now, int L, int R) {
if (L == R) {
pii tmp = (o.first) ? merge (o, tree[now]) : tree[now];
if (tmp.first) {
o = tmp; return 1;
}
return 0;
}
int mid = L + R >> 1;
if (! tree[now << 1].first) return query (now << 1, L, mid);
else {
pii tmp = (o.first) ? merge (o, tree[now << 1]) : tree[now << 1];
if (! tmp.first) return query (now << 1, L, mid);
else {
o = tmp;
return mid - L + 1 + query (now << 1 | 1, mid + 1, R);
}
}
}
}using namespace segtree;
int main () {
n = read ();
rep (i, 1, n) v[i] = read () + 1, s[v[i]] = i;
rep (i, 2, n) connect (read (), i);
DFS1 (1, 0, 1), DFS2 (1, 1), build (1, 1, n);
int T = read ();
rep (I, 1, T) {
int opt = read ();
if (opt == 1) {
int x = read (), y = read (); swap (v[x], v[y]);
update (1, 1, n, x), update (1, 1, n, y);
}
if (opt == 2) {
o = mp (0, 0);
printf ("%d\n", query (1, 1, n));
}
}
return 0;
}