zoukankan      html  css  js  c++  java
  • @codeforces


    @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
    ", segT.query());
    	}
    }
    

    @details@

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

  • 相关阅读:
    进入Docker 报错
    Oauth一直无限重定向
    jquery.click() not working in iOS
    IOS 系统 form 表单提交的问题;一直当前页面刷新
    Word 搜索功能显示bug
    百度脑图bug
    微信分享链接添加参数后缩略图不显示如何解决?
    微信分享到朋友圈 QQ空间 代码实现
    文献复现 | The support of human genetic evidence for approved drug indications
    生物医药领域 | 知名投资人 | VC | PE | IBD | 公募 | 私募
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11697918.html
Copyright © 2011-2022 走看看