雅礼集训
[雅礼集训Day1]
真•神仙打架,考出来9分,暴力打炸?!总结一下第一题。
一道经典的支持换根的树链剖分,需要支持的操作有:
1.将根设为root.
2.设u, v的最近公共祖先为p, 将p的子树中的所有点的权值加上x.
3.查询u的子树中的所有点的权值和.
对于这一类题目,其实我们并不需要真正去换根,我们只需要记录一下当前的根是谁,然后在更改和查询的时候修改一下即可。
这道题真正的难点在于如何确定lca
题解:
经过一些简单的讨论可以知道,\(u\)和\(v\)在以\(root\)为根时的\(lca\)为:\(lca(u, v)\),\(lca(u, root)\),\(lca(v, root)\)中原来深度最大的一个,其中\(lca(a, b)\)代表\(a\),\(b\)在以\(1\)为根时的\(lca\)。
太菜了并不会证,所以记一下结论吧
确定了\(lca\)之后就好做了,对于查询操作,判断当前\(root\)是否在\(u\)的子树中,
如果不在,直接查询原来的子树。
如果在,运用dfs序的连续性,排除\(root\)所在儿子的子树,查询其他即可。画图模拟一下很好看出来。
注意特判\(u\)是否是\(root\)因为这个调了好久
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<queue>
#define debug cout<<"....."<<endl;
#define lll long long
#define ll(x) (x*2)
#define rr(x) (x*2+1)
using namespace std;
lll read()
{
lll x=0,w=1;char ch=getchar();
while(ch>'9'||ch<'0') {if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*w;
}
lll n,q,cnt,m,opt,x,y,z,node;
lll head[300010],deep[300010],a[300010],size[300010],s[300010],pos[3000010];
lll sgm[1200010],lazy[1200010];
lll f[300010][21];
struct node{
lll to,next;
}edge[600010];
void add(lll x,lll y)
{
cnt++;edge[cnt].to=y;edge[cnt].next=head[x];head[x]=cnt;
}
void init()
{
for(lll i=1;i<=20;i++)
for(lll j=1;j<=n;j++)
f[j][i]=f[f[j][i-1]][i-1];
}
void dfs(lll k,lll fa)
{
lll v;
s[++m]=k;pos[k]=m;
for(lll i=head[k];i;i=edge[i].next)
{
v=edge[i].to;
if(v==fa) continue;
deep[v]=deep[k]+1;f[v][0]=k;
dfs(v,k);
size[k]+=size[v];
}
size[k]++;
}
lll LCA(lll x,lll y)
{
if(deep[x]<deep[y]) swap(x,y);
for(lll i=20;i>=0;i--)
if(deep[f[x][i]]>=deep[y]) x=f[x][i];
if(x==y) return x;
for(lll i=20;i>=0;i--)
if(f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
return f[x][0];
}
void push_down(lll root,lll l,lll r)
{
lll mid=(l+r)/2;
if(!lazy[root]) return;
sgm[ll(root)]+=(mid-l+1)*lazy[root];sgm[rr(root)]+=(r-mid)*lazy[root];
lazy[ll(root)]+=lazy[root];lazy[rr(root)]+=lazy[root];lazy[root]=0;
return;
}
void build(lll root,lll l,lll r)
{
if(l>r) return;
if(l==r)
{
sgm[root]=a[s[l]];
return;
}
lll mid=(l+r)/2;
if(l<=mid) build(ll(root),l,mid);
if(r>mid) build(rr(root),mid+1,r);
sgm[root]=sgm[ll(root)]+sgm[rr(root)];
}
void insert(lll root,lll l,lll r,lll left,lll right,lll v)
{
if(l>r) return;
if(l>right||r<left) return;
if(left<=l&&r<=right)
{
sgm[root]+=(r-l+1)*v;lazy[root]+=v;
return;
}
lll mid=(l+r)/2;
push_down(root,l,r);
if(l<=mid) insert(ll(root),l,mid,left,right,v);
if(r>mid) insert(rr(root),mid+1,r,left,right,v);
sgm[root]=sgm[ll(root)]+sgm[rr(root)];
}
lll query(lll root,lll l,lll r,lll left,lll right)
{
if(l>r) return 0;
if(l>right||r<left) return 0;
if(left<=l&&r<=right) return sgm[root];
lll mid=(l+r)/2;
push_down(root,l,r);
return query(ll(root),l,mid,left,right)+query(rr(root),mid+1,r,left,right);
}
int main()
{
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
n=read();q=read();node=1;
for(lll i=1;i<=n;i++) a[i]=read();
for(lll i=1;i<n;i++)
{
x=read();y=read();
add(x,y);add(y,x);
}
deep[1]=1;
dfs(1,0);
build(1,1,n);
init();
for(lll i=1;i<=q;i++)
{
opt=read();
if(opt==1)
node=read();
else if(opt==2)
{
x=read();y=read();z=read();
lll lca,lca1=LCA(x,y),lca2=LCA(x,node),lca3=LCA(y,node);
if(deep[lca1]>=deep[lca2]&&deep[lca1]>=deep[lca3]) lca=lca1;
else if(deep[lca2]>=deep[lca1]&&deep[lca2]>=deep[lca3]) lca=lca2;
else if(deep[lca3]>=deep[lca2]&&deep[lca3]>=deep[lca1]) lca=lca3;
lll l=LCA(lca,node);
if(lca==node) insert(1,1,n,1,n,z);
else if(l==lca&&lca!=node)
{
l=node;
for(lll i=20;i>=0;i--)
if(deep[f[l][i]]>deep[lca]) l=f[l][i];
insert(1,1,n,1,n,z);insert(1,1,n,pos[l],pos[l]+size[l]-1,-z);
}
else insert(1,1,n,pos[lca],pos[lca]+size[lca]-1,z);
}
else if(opt==3)
{
x=read();
lll l=LCA(x,node);
if(x==node) printf("%lld\n",query(1,1,n,1,n));
else if(l==x&&x!=node)
{
l=node;
for(lll i=20;i>=0;i--)
if(deep[f[l][i]]>deep[x]) l=f[l][i];
printf("%lld\n",query(1,1,n,1,pos[l]-1)+query(1,1,n,pos[l]+size[l],n));
}
else printf("%lld\n",query(1,1,n,pos[x],pos[x]+size[x]-1));
}
}
return 0;
}