遥远的国度

一、题目

点此看题

二、解法

这道题换根操作很有意思,容易发现换根对修改是没有影响的,考虑根对询问 u u u的影响:

  • u u u就是 r t rt rt,直接输出整棵树的最小值
  • r t rt rt u u u的子树内,考虑找出 u u u r t rt rt方向上的第一个儿子 v v v,答案就是全集-v子树,由于 v v v子树的连续的一段,我们可以把询问拆成两段,分别是 [ 1 , n u m v − 1 ] , [ n u m v + s i z v , n ] [1,num_v-1],[num_v+siz_v,n] [1,numv1],[numv+sizv,n] n u m num num是树剖的编号, s i z siz siz是子树的大小。
  • r t rt rt u u u的子树为,没影响,直接询问 [ n u m u , n u m u + s i z u − 1 ] [num_u,num_u+siz_u-1] [numu,numu+sizu1]的最小值。
#pragma GCC optimize(2)
#include <cstdio>
#include <cstring>
#include <iostream>
#include <map>
#define int long long
#define inf (1ll<<60)
using namespace std;
const int M = 100005;
int read()
{
    int x=0,flag=1;char c;
    while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
    while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*flag;
}//区间修改,区间最小值 
int n,m,rt,tot,a[M],c[M],siz[M],f[M],fa[M];
int Index,son[M],dep[M],num[M],top[M];
struct edge
{
    int v,next;
    edge(int V=0,int N=0) : v(V) , next(N) {}
}e[2*M];
struct node
{
	int mi,la;
	node(int M=0,int L=0) : mi(M) , la(L) {}
	node operator + (const node &B) const {
		return node(min(mi,B.mi),0);
	}
}tr[4*M];
void dfs1(int u,int p)
{
    fa[u]=p;
    dep[u]=dep[p]+1;
    siz[u]=1;
    for(int i=f[u];i;i=e[i].next)
    {
        int v=e[i].v;
        if(v==p) continue;
        dfs1(v,u);
        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;
    c[Index]=a[u];
    if(son[u]) dfs2(son[u],tp);
    for(int i=f[u];i;i=e[i].next)
        if(e[i].v^son[u] && e[i].v^fa[u])
            dfs2(e[i].v,e[i].v);
}
void build(int i,int l,int r)
{
    if(l==r)
    {
        tr[i]=node(c[l],0);
        return ;
    }
    int mid=(l+r)>>1;
    build(i<<1,l,mid);
    build(i<<1|1,mid+1,r);
    tr[i]=tr[i<<1]+tr[i<<1|1];
}
void down(int i)
{
	if(tr[i].la==0) return ;
	tr[i<<1]=tr[i<<1|1]=node(tr[i].la,tr[i].la);
	tr[i].la=0;
}
void updata(int i,int l,int r,int L,int R,int v)
{
    if(l>R || L>r) return ;
    if(L<=l && r<=R)
    {
    	tr[i]=node(v,v);
        return ;
    }
    down(i);
    int mid=(l+r)>>1;
    updata(i<<1,l,mid,L,R,v);
    updata(i<<1|1,mid+1,r,L,R,v);
    tr[i]=tr[i<<1]+tr[i<<1|1];
}
int query(int i,int l,int r,int L,int R)
{
    if(l>R || L>r || L>R) return inf;
    if(L<=l && r<=R) return tr[i].mi;
    down(i);
    int mid=(l+r)>>1;
    return min(query(i<<1,l,mid,L,R),query(i<<1|1,mid+1,r,L,R));
}
void modify(int u,int v,int c)
{
    while(top[u]^top[v])
    {
        if(dep[top[u]]<=dep[top[v]]) swap(u,v);
        updata(1,1,n,num[top[u]],num[u],c);
        u=fa[top[u]];
    }
    if(dep[u]<=dep[v]) swap(u,v);
    updata(1,1,n,num[v],num[u],c);
}
int Find(int x,int y)//y在x方向的第一个儿子 
{
	while(top[x]^top[y])
	{
		if(dep[top[x]]<=dep[top[y]]) swap(x,y);
		if(fa[top[x]]==y) return top[x];
		x=fa[top[x]];
	}
	return son[y];
}
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]?y:x;
}
signed main()
{
    n=read();m=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;
	}
	for(int i=1;i<=n;i++)
		a[i]=read(); 
	dfs1(1,0);
	dfs2(1,1);
	build(1,1,n);
	rt=read();
	while(m--)
	{
		int op=read();
		if(op==1)
			rt=read();
		if(op==2)
		{
			int u=read(),v=read(),c=read();
			modify(u,v,c);
		}
		if(op==3)
		{
			int u=read();
			if(u^rt && u==Lca(u,rt))//子树内 
			{
				int t=Find(rt,u);
				printf("%lld\n",min(query(1,1,n,1,num[t]-1),query(1,1,n,siz[t]+num[t],n)));
			}
			else if(u==rt)
				printf("%lld\n",query(1,1,n,1,n));
			else
				printf("%lld\n",query(1,1,n,num[u],num[u]+siz[u]-1));
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值