传送门(这篇写得比我好
qwq
s到t的链可以拆成两条纵链
令dis[x]表示x到根的距离
对于一条纵链上的一个点x,添加的数就可以看成
(dis[x]-dis[t])* k+b
=dis[x]*k-dis[t]*k+b
=k*dis[x]+B
很像直线的表达式y=kx+b
树剖,问题变成线段树维护多条直线在区间内的最小值
对于一条链,因为这个区间内的dis是递增的即x坐标递增,这个操作相当于给线段树一个区间上覆盖一条直线
在区间上打永久化标记为覆盖这个区间的直线,当遇到新的直线时,设加入直线f1,当前覆盖这个区间的直线f2,如果f1(l)< f2(l)&&f1(r)< f2(r) 那么可以用f1代替f2,都大于则没有用,否则递归下去,因为最多只有一个交点,这个交点不在左就在右,所以可以保证复杂度
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
inline ll _min(const ll a,const ll b){return a<b?a:b;}
inline void down(ll &x,const ll &y){if(x>y)x=y;}
const int maxn = 210000;
const int maxm = 210000;
const ll inf = 123456789123456789ll;
int n,m;
struct edge
{
int y,c,nex;
edge(){}
edge(const int _y,const int _c,const int _nex){y=_y;c=_c;nex=_nex;}
}a[maxn<<1]; int len,fir[maxn];
inline void ins(const int x,const int y,const int c){a[++len]=edge(y,c,fir[x]);fir[x]=len;}
ll dis[maxn];
int f[maxn],top[maxn],siz[maxn],son[maxn],dep[maxn],w[maxn];
void dfs(const int x)
{
siz[x]=1;
for(int k=fir[x];k;k=a[k].nex)
{
const int y=a[k].y;
if(y!=f[x])
{
dep[y]=dep[x]+1; dis[y]=dis[x]+(ll)a[k].c;
f[y]=x;
dfs(y);
if(siz[son[x]]<siz[y]) son[x]=y;
siz[x]+=siz[y];
}
}
}
int z;
ll s[maxn];
void build(const int x,const int tp)
{
s[w[x]=++z]=dis[x]; top[x]=tp;
if(son[x]) build(son[x],tp);
for(int k=fir[x];k;k=a[k].nex)
{
const int y=a[k].y;
if(y!=f[x]&&y!=son[x])
build(y,y);
}
}
struct segment
{
bool flag;
ll a,b,mn;
segment(){flag=false;mn=inf;}
}seg[maxn<<2];
int lx,rx;
ll fa,fb;
void up(const int x)
{
int lc=x<<1,rc=lc|1;
down(seg[x].mn,_min(seg[lc].mn,seg[rc].mn));
}
void upd(const int x,const int l,const int r)
{
if(rx<l||r<lx) return;
if(lx<=l&&r<=rx)
{
ll cl=s[l]*fa+fb,cr=s[r]*fa+fb;
if(seg[x].flag)
{
ll cl2=s[l]*seg[x].a+seg[x].b,cr2=s[r]*seg[x].a+seg[x].b;
if(cl>=cl2&&cr>=cr2) return;
else if( (cl<cl2&&cr>=cr2)||(cl>=cl2&&cr<cr2) )
{
int mid=l+r>>1,lc=x<<1,rc=lc|1;
upd(lc,l,mid); upd(rc,mid+1,r);
up(x); return;
}
}
seg[x].flag=true;
seg[x].a=fa,seg[x].b=fb;
down(seg[x].mn,_min(cl,cr));
return;
}
int mid=l+r>>1,lc=x<<1,rc=lc|1;
upd(lc,l,mid); upd(rc,mid+1,r);
up(x);
}
ll ret;
void query(const int x,const int l,const int r)
{
if(rx<l||r<lx) return;
if(lx<=l&&r<=rx) { down(ret,seg[x].mn); return ; }
int mid=l+r>>1,lc=x<<1,rc=lc|1;
if(seg[x].flag)
{
ll cl=s[max(lx,l)]*seg[x].a+seg[x].b,cr=s[min(rx,r)]*seg[x].a+seg[x].b;
down(ret,_min(cl,cr));
}
query(lc,l,mid); query(rc,mid+1,r);
}
int LCA(int x,int y)
{
int f1=top[x],f2=top[y];
while(f1!=f2)
{
if(dep[f1]<dep[f2]) swap(f1,f2),swap(x,y);
x=f[f1],f1=top[x];
}
return dep[x]<dep[y]?x:y;
}
void change(int S,int T,int A,int B)
{
int tp=LCA(S,T),ft=top[tp];
int x=S,f1=top[x];
while(dep[x]>=dep[tp])
{
if(f1==ft) f1=tp;
fa=-A,fb=B+(ll)A*dis[S],lx=w[f1],rx=w[x],upd(1,1,n);
x=f[f1],f1=top[x];
}
int y=T,f2=top[y];
while(dep[y]>=dep[tp])
{
if(f2==ft) f2=tp;
fa=A,fb=B+(ll)A*(dis[S]-dis[tp]*2ll),lx=w[f2],rx=w[y],upd(1,1,n);
y=f[f2],f2=top[y];
}
}
ll solve(int x,int y)
{
ll re=inf;
int f1=top[x],f2=top[y];
while(f1!=f2)
{
if(dep[f1]<dep[f2]) swap(f1,f2),swap(x,y);
lx=w[f1],rx=w[x],ret=inf,query(1,1,n);
down(re,ret);
x=f[f1],f1=top[x];
}
if(dep[x]>dep[y]) swap(x,y);
lx=w[x],rx=w[y],ret=inf,query(1,1,n);
down(re,ret);
return re;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++)
{
int x,y,c; scanf("%d%d%d",&x,&y,&c);
ins(x,y,c); ins(y,x,c);
}
dep[1]=1; dfs(1);
build(1,1);
for(int i=1;i<=m;i++)
{
int type,s,t; scanf("%d%d%d",&type,&s,&t);
if(type==1)
{
int A,B; scanf("%d%d",&A,&B);
change(s,t,A,B);
}
else printf("%lld\n",solve(s,t));
}
return 0;
}