CF916E Jamie and Tree

一、题目

点此看题

二、解法

建议首先看看这道题,基本上是一道题。

现在还剩下的问题就是在换根状态上求 l c a lca lca,首先这个 l c a lca lca一定在 ( u , v ) (u,v) (u,v)的路径上,而路径是不会改变的,通过模拟可以发现 l c a lca lca就是 l c a ( u , v ) lca(u,v) lca(u,v) l c a ( u , l c a ′ ) lca(u,lca') lca(u,lca) l c a ( v , l c a ′ ) lca(v,lca') lca(v,lca)其中深度最大的。

#include <cstdio>
#include <iostream>
using namespace std;
#define int long long
const int M = 100005;
int read()
{
    int num=0,flag=1;char c;
    while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
    while(c>='0'&&c<='9')num=(num<<3)+(num<<1)+(c^48),c=getchar();
    return num*flag;
}
int n,m,rt,tot,Index,f[M],a[M],fa[M][20],num[M];
int top[M],dep[M],siz[M],son[M],s[4*M],la[4*M];
struct edge
{
	int v,next;
}e[2*M];
void dfs1(int u)
{
	siz[u]=1;
	dep[u]=dep[fa[u][0]]+1;
	for(int i=1;i<20;i++)
		fa[u][i]=fa[fa[u][i-1]][i-1];
	for(int i=f[u];i;i=e[i].next)
	{
		int v=e[i].v;
		if(v==fa[u][0]) continue;
		fa[v][0]=u;
		dfs1(v);
		siz[u]+=siz[v];
		if(siz[v]>siz[son[u]]) son[u]=v;
	}
}
void dfs2(int u,int tp)
{
	top[u]=tp;
	num[u]=++Index;
	if(son[u]) dfs2(son[u],tp);
	for(int i=f[u];i;i=e[i].next)
	{
		int v=e[i].v;
		if(v^son[u] && v^fa[u][0]) dfs2(v,v);
	}
}
void upd(int i,int l,int r,int v)
{
	s[i]+=(r-l+1)*v;
	la[i]+=v;
}
void down(int i,int l,int r)
{
	if(!la[i]) return ;
	int mid=(l+r)>>1;
	upd(i<<1,l,mid,la[i]);
	upd(i<<1|1,mid+1,r,la[i]);
	la[i]=0;
} 
void ins(int i,int l,int r,int L,int R,int v)
{
	if(L>r || l>R) return ;
	if(L<=l && r<=R)
	{
		upd(i,l,r,v);
		return ;
	}
	down(i,l,r);
	int mid=(l+r)>>1;
	ins(i<<1,l,mid,L,R,v);
	ins(i<<1|1,mid+1,r,L,R,v);
	s[i]=s[i<<1]+s[i<<1|1];
}
int ask(int i,int l,int r,int L,int R)
{
	if(L>r || l>R) return 0;
	if(L<=l && r<=R) return s[i];
	down(i,l,r);
	int mid=(l+r)>>1;
	return ask(i<<1,l,mid,L,R)+ask(i<<1|1,mid+1,r,L,R);
}
int Lca(int u,int v)
{
	while(top[u]^top[v])
	{
		if(dep[top[u]]<dep[top[v]]) swap(u,v);
		u=fa[top[u]][0];
	}
	if(dep[u]>dep[v]) swap(u,v);
	return u;
}
int up(int u,int v)
{
	for(int i=19;i>=0;i--)
		if(dep[fa[v][i]]>dep[u])
			v=fa[v][i];
	return v;
}
signed main()
{
	n=read();m=read();rt=1;
	for(int i=1;i<=n;i++)
		a[i]=read();
	for(int i=1;i<n;i++)
	{
		int u=read(),v=read();
		e[++tot]=edge{v,f[u]},f[u]=tot;
		e[++tot]=edge{u,f[v]},f[v]=tot;
	}
	dfs1(1);
	dfs2(1,1);
	for(int i=1;i<=n;i++)
		ins(1,1,n,num[i],num[i],a[i]);
	for(int i=1;i<=m;i++)
	{
		int op=read(),u=read();
		if(op==1)
			rt=u;
		if(op==2)
		{
			int v=read(),c=read(),lca=Lca(u,v),t1=Lca(u,rt),t2=Lca(v,rt);
			if(dep[lca]<dep[t1]) lca=t1;
			if(dep[lca]<dep[t2]) lca=t2;
			if(lca==rt)
			{
				ins(1,1,n,1,n,c);
				continue;
			}
			if(num[lca]<=num[rt] && num[rt]<=num[lca]+siz[lca]-1)
			{
				int t=up(lca,rt);
				ins(1,1,n,1,n,c);
				ins(1,1,n,num[t],num[t]+siz[t]-1,-c);
			}
			else
				ins(1,1,n,num[lca],num[lca]+siz[lca]-1,c);
		}
		if(op==3)
		{
			if(u==rt)
			{
				printf("%lld\n",ask(1,1,n,1,n));
				continue ;
			}
			if(num[u]<=num[rt] && num[rt]<=num[u]+siz[u]-1)
			{
				int t=up(u,rt);
				printf("%lld\n",ask(1,1,n,1,n)-ask(1,1,n,num[t],num[t]+siz[t]-1));
			}
			else
				printf("%lld\n",ask(1,1,n,num[u],num[u]+siz[u]-1));
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值