CF916E Jamie and Tree
分析
换根树剖
这类题目不多,一般都是需要分类讨论。
题意:
支持以下操作:
- 换根
- 子树修改
- LCA
- 子树查询
我们挨个分析
先以1为根将整棵树剖一遍
接着处理每个操作:
1.直接换
2.我们要分类讨论,为了叙述方便,记x,y在原树中的LCA为lca(x,y)
对于任意一点x,有以下几种情况:
(1)x=root,修改整棵树
(2)lca(x,root)!=x,那么换根不影响子树,直接修改
(3)lca(x,root)=x,则我们直接找到从root到x的路径中,x的直接儿子,将直接儿子的子树除去,其余部分都是x的子树。
3.我们依然要分类讨论:
我们不妨默认\(dep[x]\leq dep[y]\)
进行分类讨论:
(1)lca(x,y)=x
1)root在y的子树中,那么答案为y
2)root在x与y之间,那么答案为root
3)root在其他位置,那么答案为x
(2)lca(x,y)!=x
1)root在x的子树中,那么答案为x
2)root在y的子树中,那么答案为y
3)root在x到y的路径上,那么答案为root
4)若lca(x,root)=lca(y,root),即root在下图所示位置,答案为lca(x,y)
5)若lca(x,y)!=lca(x,root),即root在下图位置,答案为lca(x,root)
6)若lca(x,y)!=lca(y,root),即root在下图位置,答案为lca(y,root)
4.同2,进行分类讨论查询
Ac_code
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10,M = N*2;
const LL INF = 1e18;
struct Node
{
int l,r;
LL sum,tag;//可能加负数
}tr[N<<2];
int h[N],e[M],ne[M],w[N],idx;
int sz[N],son[N],fa[N],dep[N];
int top[N],id[N],nw[N],ts;
int n,q,root;
void add(int a,int b)
{
e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}
void dfs1(int u,int pa,int depth)
{
sz[u] = 1,dep[u] = depth;
for(int i=h[u];~i;i=ne[i])
{
int j = e[i];
if(j==pa) continue;
fa[j] = u;
dfs1(j,u,depth+1);
sz[u] += sz[j];
if(sz[j]>sz[son[u]]) son[u] = j;
}
}
void dfs2(int u,int tp)
{
top[u] = tp,id[u] = ++ts,nw[ts] = w[u];
if(!son[u]) return ;
dfs2(son[u],tp);
for(int i=h[u];~i;i=ne[i])
{
int j = e[i];
if(j==son[u]||j==fa[u]) continue;
dfs2(j,j);
}
}
void pushup(int u)
{
tr[u].sum = tr[u<<1].sum + tr[u<<1|1].sum;
}
void build(int u,int l,int r)
{
if(l==r)
{
tr[u] = {l,r,nw[l],0};
return ;
}
tr[u] = {l,r};
int mid = l + r >> 1;
build(u<<1,l,mid),build(u<<1|1,mid+1,r);
pushup(u);
}
void pushdown(int u)
{
auto &root = tr[u],&left = tr[u<<1],&right = tr[u<<1|1];
if(root.tag)
{
left.tag += root.tag;
left.sum += root.tag*(left.r - left.l + 1);
right.tag += root.tag;
right.sum += root.tag*(right.r - right.l + 1);
root.tag = 0;
}
}
void modify(int u,int l,int r,int k)
{
if(l<=tr[u].l&&tr[u].r<=r)
{
tr[u].tag += k;
tr[u].sum += 1ll*k*(tr[u].r - tr[u].l + 1);
return ;
}
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if(l<=mid) modify(u<<1,l,r,k);
if(r>mid) modify(u<<1|1,l,r,k);
pushup(u);
}
LL query(int u,int l,int r)
{
if(l<=tr[u].l&&tr[u].r<=r) return tr[u].sum;
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
LL res = 0;
if(l<=mid) res += query(u<<1,l,r);
if(r>mid) res += query(u<<1|1,l,r);
return res;
}
int lca(int x,int y)
{
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]]) swap(x,y);
x = fa[top[x]];
}
return dep[x]<dep[y]?x:y;
}
int find(int x)
{
int u = root;
while(top[u]!=top[x])
{
if(fa[top[u]]==x) return top[u];
u = fa[top[u]];
}
return son[x];
}
int LCA(int x,int y)
{
if(dep[x]>dep[y]) swap(x,y);
if(lca(x,y)==x)
{
if(id[root]>=id[y]&&id[root]<=id[y]+sz[y]-1) return y;
if(lca(x,root)==x) return lca(y,root);
return x;
}
if(id[root]>=id[x]&&id[root]<=id[x]+sz[x]-1) return x;
if(id[root]>=id[y]&&id[root]<=id[y]+sz[y]-1) return y;
if((lca(x,root)==root&&lca(x,y)==lca(y,root))||(lca(y,root)==root&&lca(x,y)==lca(x,root))) return root;
if(lca(root,x)==lca(y,root)) return lca(x,y);
if(lca(x,y)!=lca(x,root)) return lca(x,root);
return lca(y,root);
}
void mo2(int x,int p)
{
if(root==x)
{
modify(1,1,n,p);
return ;
}
int res = lca(root,x);
if(res!=x) modify(1,id[x],id[x]+sz[x]-1,p);
else
{
int u = find(x);
modify(1,1,n,p);
modify(1,id[u],id[u]+sz[u]-1,-p);
}
}
LL q2(int x)
{
if(x==root) return query(1,1,n);
int res = lca(root,x);
if(res!=x) return query(1,id[x],id[x]+sz[x]-1);
int u = find(x);
return query(1,1,n) - query(1,id[u],id[u]+sz[u]-1);
}
int main()
{
scanf("%d%d",&n,&q);
memset(h,-1,sizeof h);
for(int i=1;i<=n;i++) scanf("%d",w+i);
for(int i=0;i<n-1;i++)
{
int u,v;scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
root = 1;
dfs1(1,-1,1);
dfs2(1,1);
build(1,1,n);
while(q--)
{
int op,x,y,c;
scanf("%d%d",&op,&x);
if(op==1) root = x;
else if(op==2)
{
scanf("%d%d",&y,&c);
mo2(LCA(x,y),c);
}
else printf("%lld\n",q2(x));
}
return 0;
}
本博客思路,主要来自