题意:
分析:
首先树上路径操作,要用树剖
其次,操作等价于区间加一条线段,然后查询区间最小值
但是与普通李超树不同,这次 \(x\) 值并不连续,每次插入的线段下标是和给定起点的距离,那么我们更改一下维护的信息,线段树上 \(a\) 点的 $x $ 值是它离根的距离,然后我们将操作转化,每次插入的线段分成两条
- \(s\to lca\)
这一部分距离随编号减小而增大,所以斜率由 \(k\) 变为 \(-k\),\(b\) 的初值就是 \(dis[s]*k+b\)
- \(lca\to t\)
这一部分距离随编号增大而增大,,那么斜率不变, \(b\) 值变为 \((dis[s]-(dis[lca]*2)*k+b)\)
之后就是维护区间最小值,但注意由于标记永久化的操作,我们需要每一次将答案和对应区间内 \(max(l,ql)\) ~ \(min(r,qr)\) 的部分的最小值取 \(min\)
代码:
#include<bits/stdc++.h>
#define lc (rt<<1)
#define rc (rt<<1)|1
using namespace std;
namespace zzc
{
long long read()
{
long long x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
const int maxn = 1e5+5;
long long n,m,cnt,idx;
long long head[maxn],siz[maxn],fa[maxn],son[maxn],top[maxn],dep[maxn],dfn[maxn],dis[maxn],id[maxn];
struct edge
{
int to,nxt,val;
}e[maxn<<1];
void add(int u,int v,int w)
{
e[++cnt].to=v;
e[cnt].nxt=head[u];
e[cnt].val=w;
head[u]=cnt;
}
struct line
{
long long k,b;
line (long long k=0,long long b=0):k(k),b(b){}
long long calc(long long pos)
{
return k*pos+b;
}
};
struct node
{
line L;
long long val;
bool flag;
node(line L,long long val,bool flag=false):L(L),val(val),flag(flag){}
node(){}
}t[maxn<<2];
void dfs1(int u,int ff)
{
dep[u]=dep[ff]+1;fa[u]=ff;siz[u]=1;son[u]=0;
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].to;
if(v==ff) continue;
dis[v]=dis[u]+e[i].val;
dfs1(v,u);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]]) son[u]=v;
}
}
void dfs2(int u,int bel)
{
top[u]=bel;dfn[u]=++idx;id[idx]=u;
if(son[u]) dfs2(son[u],bel);
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].to;
if(v==fa[u]||v==son[u]) continue;
dfs2(v,v);
}
}
void pushup(int rt)
{
t[rt].val=min(t[rt].val,min(t[lc].val,t[rc].val));
}
void build(int rt,int l,int r)
{
t[rt]=node(line(0,123456789123456789ll),123456789123456789ll,true);
if(l==r) return ;
int mid=(l+r)>>1;
build(lc,l,mid);build(rc,mid+1,r);
pushup(rt);
}
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;
}
void update(int rt,int l,int r,int ql,int qr,line x)
{
if(ql<=l&&r<=qr)
{
long long ll=dis[id[l]];
long long rr=dis[id[r]];
long long mid=(l+r)>>1,mmid=dis[id[mid]];
long long lp=x.calc(ll),rp=x.calc(rr);
long long lq=t[rt].L.calc(ll),rq=t[rt].L.calc(rr);
if(!t[rt].flag) t[rt].flag=true,t[rt].L=x;
else if(lp<lq&&rp<rq) t[rt].L=x;
else if(lp<lq||rp<rq)
{
if(x.calc(mmid)<t[rt].L.calc(mmid)) swap(x,t[rt].L);
if(x.calc(ll)<t[rt].L.calc(ll)) update(lc,l,mid,ql,qr,x);
else update(rc,mid+1,r,ql,qr,x);
}
t[rt].val=min(t[rt].val,min(t[rt].L.calc(ll),t[rt].L.calc(rr)));
if(l!=r) pushup(rt);
return ;
}
int mid=(l+r)>>1;
if(ql<=mid) update(lc,l,mid,ql,qr,x);
if(qr>mid) update(rc,mid+1,r,ql,qr,x);
if(l!=r) pushup(rt);
}
long long query(int rt,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr) return t[rt].val;
int mid=(l+r)>>1;
long long res=123456789123456789ll;
if(t[rt].flag) res=min(res,min(t[rt].L.calc(dis[id[max(l,ql)]]),t[rt].L.calc(dis[id[min(r,qr)]])));
if(ql<=mid) res=min(res,query(lc,l,mid,ql,qr));
if(qr>mid) res=min(res,query(rc,mid+1,r,ql,qr));
return res;
}
void update_tree(int u,int v,long long k,long long b)
{
line x=line(k,b);
while(top[u]!=top[v])
{
update(1,1,n,dfn[top[u]],dfn[u],x);
u=fa[top[u]];
}
update(1,1,n,dfn[v],dfn[u],x);
}
long long query_tree(int s,int t)
{
long long res=123456789123456789;
while(top[s]!=top[t])
{
if(dep[top[s]]<dep[top[t]]) swap(s,t);
res=min(res,query(1,1,n,dfn[top[s]],dfn[s]));
s=fa[top[s]];
}
if(dep[s]>dep[t]) swap(s,t);
res=min(res,query(1,1,n,dfn[s],dfn[t]));
return res;
}
void work()
{
long long opt,s,t,a,b,c;
n=read();m=read();
for(int i=1;i<n;i++)
{
a=read();b=read();c=read();
add(a,b,c);add(b,a,c);
}
dfs1(1,0);dfs2(1,1);
build(1,1,n);
for(int i=1;i<=m;i++)
{
opt=read();s=read();t=read();
if(opt==1)
{
a=read();b=read();
int tmp=lca(s,t);
update_tree(s,tmp,-a,dis[s]*a+b);
update_tree(t,tmp,a,(dis[s]-(dis[tmp]<<1))*a+b);
}
else
{
printf("%lld\n",query_tree(s,t));
}
}
}
}
int main()
{
zzc::work();
return 0;
}