Loading

CF916E Jamie and Tree

CF916E Jamie and Tree

强制换根操作,与【遥远的国度】比较类似。由于操作对于某个LCA进行子树操作,实际难度高于相似题目。
不光要懂得拼凑加法,还要懂得容斥,进行减法。

思路

对于1操作直接换。对于2操作,由于进行了换根操作,LCA的确定也需要 进行分类讨论。

对于已知操作点x,考虑x要对换根前(root=1) 的那些位置进行操作。

  • \(x=root\),对整棵树进行操作
  • \(root\)不在\(x\)的子树中,对\(x\)子树进行操作即可。
  • \(root\)\(x\)的子树中, 应当对\(x\)的祖先以及之外的部分进行操作。通过容斥原理,先对整颗子树进行操作再对\(x\to root\)路径上的x的儿子的子树进行一个相反操作即可。image

之后对于LCA的处理,继续进行分类讨论。

  • \((x,y)\)位于\(root\)的子树中,LCA不发生改变。
  • \((x,y)\)中其中一个位于\(root\)的子树中,则LCA=root。
  • \((x,y)\)均不在\(root\)的子树中,不太好发现,可以根据手玩发现LCA\((x,y)\)为LCA\((x,root)\),LCA\((y,root)\)中深度更大的。

但是如何判断x,y是否在root的子树中也是不太好处理的(可以通过dfs序判断是否位于子树中),对三种情况进行一下总结会发现得到\(lca\)就是LCA\((x,y)\),LCA\((x,root)\),LCA\((y,root)\)中深度更大的那个点。

代码

注意预处理多级父亲时不要忘记u=1,不能fa[j][k]而是要fa[u][k]

本题没有路径上的操作“半个”树剖就够了。

inline void dfs(int u,int father,int depth)
{
	fa[u][0] = father,sz[u] = 1,dep[u] = depth;
	dfn[u] = ++timestamp,nw[timestamp] = w[u];
	for(int k = 1;k <= 17;k++) fa[u][k] = fa[fa[u][k - 1]][k - 1];
	for(int i = h[u];~i;i = ne[i]){
		int j = e[i];
		if(j == father) continue;
		dfs(j,u,depth + 1);
		sz[u] += sz[j];
	}
}

inline int Get_lca(int a,int b)
{
	int lca1 = LCA(a,b);
	int lca2 = LCA(a,root);
	int lca3 = LCA(b,root);
	if(dep[lca1] > dep[lca2] && dep[lca1] > dep[lca3]) return lca1;
	if(dep[lca2] > dep[lca1] && dep[lca2] > dep[lca3]) return lca2;
	return lca3;
}

inline int Jump(int fr,int depth)/*非常妙的跳跃*/
{
	for(int k = 17;~k;k--) if(depth & (1 << k)) fr = fa[fr][k];
	return fr;
}

inline void Update_tree(int u,int k)
{
	if(u == root){Modify(1,1,n,k);return ;}
	int L = dfn[u],R = dfn[u] + sz[u] - 1;
	if(dfn[root] > R || dfn[root] < L){Modify(1,L,R,k);return ;}
	int p = Jump(root,dep[root] - dep[u] - 1);
	Modify(1,1,n,k);
	Modify(1,dfn[p],dfn[p] + sz[p] - 1,-k);
}

inline int Query_tree(int u)
{
	if(u == root) return Query(1,1,n);
	int L = dfn[u],R = dfn[u] + sz[u] - 1;
	if(dfn[root] > R || dfn[root] < L) return Query(1,L,R);
	int res = 0;
	res = Query(1,1,n);
	int p = Jump(root,dep[root] - dep[u] - 1);
	res -= Query(1,dfn[p],dfn[p] + sz[p] - 1);
	return res;
}

signed main()
{
	memset(h,-1,sizeof h);
	n = read();Q = read();
	for(int i = 1;i <= n;i++) w[i] = read();
	for(int i = 1;i < n;i++){
		int a,b;
		a = read();b = read();
		add(a,b),add(b,a);
	}
	
	dfs(1,0,1);
	Build(1,1,n);
	root = 1;

	while(Q--)
	{
		int op = read();
		if(op == 1) root = read();
		if(op == 2){
			int x,y,k;
			x = read();y = read(),k = read();
			int lca = Get_lca(x,y);
			Update_tree(lca,k);
		}
		if(op == 3){
			int u = read();
			printf("%lld\n",Query_tree(u));
		}
	}
	
	return 0;
} 
posted @ 2021-09-02 20:23  场-room  阅读(30)  评论(0)    收藏  举报