一.闲话

学了淀粉质有一年多了,期间基本没用过,又因为当时是直接背的代码,导致打比赛的时候对这道题根本无从下手,甚至都没想到是淀粉质qwq

然后,今天去学了一下淀粉质,发现原理和实现特别简单,快速码了个模板后,又去顺带学了下点分树,就来搞这道题了。。。

二.题解

这道题,我们看数据范围,发现成熟度的大小非常的小,所以,我们考虑下如何计算答案。

假设数据随机,我们把树减号后,期望树高是logn。

这样,我们可以,对于每一个点,我们算出他的子树里面所有点到这个子树的距离。复杂度【每个点最多只有logn个祖先,所以每个点最多被算logn次】

然后,求出这个之后,我们就可以开一个动态加点线段树来做了,我们对每个点i开一点动态加点线段树,里面每个节点表示的是,i的子树中,成熟度在l-r的距离i最小的节点到i的距离

同样使用上面的证明方法,每个点最多被放进logn棵线段树中,所以,加上线段树的复杂度就是:

再考虑1操作,其实和上面存值的操作一样的。

关于2操作,我们可以求出u的所有祖先(包括u)的子树中,成熟度为l-r的点到这个点的最小距离(访问一遍线段树即可),然后加上u到这个点的距离就行了,最后所有答案取最小再乘2(还要走回去)就是我们要求的答案了~

可能有些人有点疑惑,假如,我们对于u的一个祖先x,他的子树中成熟度符合条件的到u距离最小的点到x的路径中是有可能会走到u到x的路径的一部分的,这样的话,我们计算的时候,那部分不就重复计算了吗?但是,画个图我们就会发现,其实如果我们有部分走重了话那么,我们计算既是x的儿子,又是u的祖先的那个点y时,我们就少走了一段y到x的距离,所以,我们在算y的时候的答案就会算x的时候小,同理,如果y也有重叠的话,另一个点z就会比y算的小。。。由于我们是取最小,所以最终我们的答案一定没有重叠部分

然而,我们的做法有一个前提,就是数据随机。

如果给出一个链的话,复杂度就会原地爆炸

怎么办呢?

这里,就要用到一个神奇的东西了——点分树

点分树是什么东西?

我们先把原来的树的重心x做为根,在把x的各个儿子所在的子树的重心作为x的儿子。。。一直递归下去,最终弄出来的树就是点分树。

点分树很神奇,因为我们是把子树的重心作为根,所以,最终点分树的树高一定是logn级别的

而此题,我们其实并不需要知道我们诸如谁是谁的父亲之类的信息,我们需要知道的只是一个点到其子树所有点的距离,而这个东西,我们是可以在构造点分树时,一遍dfs直接求出来,所以我们可以建一个树高为logn的点分树,再用之前的方法,构造动态开点线段树就可以解决此题了~

代码:

#pragma GCC optimize(3,"inline","Ofast")
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+1,inf=2e9,M=1e7+1;
struct node{
    int v,w,nex;
}t[N<<1];
struct zxs{
    int lson,rson,w;
}p[M];
int dis[N][30],dep[N];//i到深度为j的祖先的距离
int fa[N];
int val[N],siz[N],tot,root;
int w[N],las[N],len;
bool vis[N];
int id[N];
inline void add(int u,int v,int w){
    t[++len]=(node){v,w,las[u]},las[u]=len;
}
inline void find_zx(int now,int fu){
    siz[now]=1;val[now]=0;
    for(int i=las[now];i;i=t[i].nex){
        int v=t[i].v;
        if(!vis[v]&&fu!=v){
            find_zx(v,now);
            siz[now]+=siz[v];
            val[now]=max(val[now],siz[v]);
        }
    }
    val[now]=max(val[now],tot-siz[now]);
    if(val[now]<val[root]){
        root=now;
    }
}
int num;
inline void find_dis(int now,int fu,int Dis){
    dis[now][num]=Dis;
    for(int i=las[now];i;i=t[i].nex){
        int v=t[i].v;
        if(vis[v]||v==fu){
            continue;
        }
        find_dis(v,now,Dis+t[i].w);
    }
}
inline void work(int now,int fu){
    vis[now]=1;fa[now]=fu;num=dep[now];
    find_dis(now,now,0);
    for(int i=las[now];i;i=t[i].nex){
        int v=t[i].v;
        if(vis[v]){
            continue;
        }
        root=0;tot=siz[v],find_zx(v,v);
        dep[root]=dep[now]+1;
        work(root,now);
    }
}
int cnt;
inline void insert(int &now,int l,int r,int x,int y){
    if(!now){
        now=++cnt;p[now].w=inf;
    }
    p[now].w=min(p[now].w,y);
    if(l==r){
        return;
    }
    int mid=(l+r)>>1;
    if(x<=mid){
        insert(p[now].lson,l,mid,x,y);
    }else{
        insert(p[now].rson,mid+1,r,x,y);
    }
}
inline void Insert(int x,int val){
    int now=x;
    while(now){
        insert(id[now],1,10000,val,dis[x][dep[now]]),now=fa[now];
    }
}
inline int find(int now,int l,int r,int lc,int rc){
    if(!now){
        return inf;
    }
    if(lc<=l&&r<=rc){
        return p[now].w;
    }
    int mid=(l+r)>>1,res=inf;
    if(lc<=mid){
        res=min(res,find(p[now].lson,l,mid,lc,rc));
    }
    if(rc>mid){
        res=min(res,find(p[now].rson,mid+1,r,lc,rc));
    }
    return res;
}
inline int Find(int x,int l,int r){
    int now=x,ans=inf;
    while(now){
        ans=min(ans,find(id[now],1,10000,l,r)+dis[x][dep[now]]),now=fa[now];
    }
    return ans>=inf?-1:ans;
}
inline int read(){
    char ch=getchar();int w=0,ss=0;
    while(ch<'0'||ch>'9')w|=(ch=='-'),ch=getchar();
    while(ch>='0'&&ch<='9')ss=(ss*10+(ch-'0')),ch=getchar();
    return w?-ss:ss;
}
inline void write(int x){
    if(x<0)putchar('-'),x=-x;
    if(x>9)write(x/10);
    putchar(char(x%10+'0'));
}
int main(){
    int n=read(),m=read();
    for(int i=1;i<=n;++i){
        w[i]=read();
    }
    for(int i=1;i<n;++i){
        int u=read(),v=read(),w=read();
        add(u,v,w),add(v,u,w);
    }
    tot=n;
    val[root]=inf;
    find_zx(1,1);
    work(root,0);
    for(int i=1;i<=n;++i){
        Insert(i,w[i]);
    }
    while(m--){
        int opt=read();
        if(opt==1){
            int u=read(),x=read();
            Insert(u,x);
        }else{
            int u=read(),x=read(),y=read();
            int res=Find(u,x,y);
            if(res==-1){
                puts("-1");
            }else{
                write(res*2),putchar('\n');
            }
        }
    }
    return 0;
}