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的儿子的子树进行一个相反操作即可。

之后对于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;
}
艰难困苦,玉汝于成

浙公网安备 33010602011771号