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

posted @ 2019-10-18 13:51  Tiw_Air_OAO  阅读(152)  评论(0编辑  收藏  举报