第一次接触到这么神奇的线段树。
首先树的形态不改变所以数链剖分+线段树建树。对于线段树上的每一个点,我们保证其最多保存一条直线,如果存在第二条直线,那么两条直线“占据空间”较小的一个一定可以下传到它的某个儿子,而这个节点只存占据空间较大的一个即可。
查询的时候要用到永久化(好像是叫这个奇怪的名字)思想,一边向下查一边比较存放在当前节点的直线能否更新最小值。
/**************************************************************
Problem: 4515
User: RicardoWang
Language: C++
Result: Accepted
Time:21920 ms
Memory:38232 kb
****************************************************************/
#include<cstdlib>
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
#define oo 123456789123456789ll
#define maxn 200005
void _read(int &x){x=0; char ch=getchar(); bool flag=false;while(ch<'0'||ch>'9'){if(ch=='-')flag=true;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}if(flag)x=-x; return ;}
void _readLL(long long &x){x=0; char ch=getchar(); bool flag=false;while(ch<'0'||ch>'9'){if(ch=='-')flag=true;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}if(flag)x=-x; return ;}
struct edge{int to,d,next;}e[maxn*2]; int edge_ct,head[maxn]; void add(int x,int y,int z){e[++edge_ct]=(edge){y,z,head[x]};head[x]=edge_ct;e[++edge_ct]=(edge){x,z,head[y]};head[y]=edge_ct;}
int n,m,son[maxn],sz[maxn],d[maxn],tp[maxn],tr[maxn],f[maxn],_time,_tr[maxn],fd[maxn];
long long dis[maxn];
void DFS_1(int now,int fa)
{
sz[now]=1; son[now]=0; d[now]=d[fa]+1; f[now]=fa; dis[now]=dis[fa]+fd[now];
for(int id=head[now];id;id=e[id].next){if(e[id].to==fa)continue;fd[e[id].to]=e[id].d;DFS_1(e[id].to,now);sz[now]+=sz[e[id].to];if(sz[e[id].to]>sz[son[now]])son[now]=e[id].to;} return ;
}
void DFS_2(int now,int top)
{
tr[now]=++_time;tp[now]=top; if(son[now])DFS_2(son[now],top);
for(int id=head[now];id;id=e[id].next){if(e[id].to==f[now] || e[id].to==son[now])continue; DFS_2(e[id].to,e[id].to);}return ;
}
void Init(){int x,y,z;_read(n);_read(m);for(int i=1;i<n;i++){_read(x);_read(y);_read(z);add(x,y,z);}DFS_1(1,0);DFS_2(1,1);return ;}
int np,rt,chi[2*maxn][2];bool tag[maxn*2];
long long d_first[2*maxn],dsum[2*maxn],minv[maxn];
void build(int &now,int L,int R){now=++np; tag[now]=false; minv[now]=oo;if(L==R){d_first[now]=dsum[now]=fd[_tr[L]];return;}int m=(L+R)>>1;build(chi[now][0],L,m);build(chi[now][1],m+1,R);d_first[now]=d_first[chi[now][0]];dsum[now]=dsum[chi[now][0]]+dsum[chi[now][1]];}
int LCA(int x,int y){ while(tp[x]!=tp[y]){if(d[tp[x]]>=d[tp[y]])x=f[tp[x]];else y=f[tp[y]];} return d[x]>d[y] ? y:x;}
long long getsum(int now,int L,int R,int x,int y)
{
if(x<=L && R<=y)return dsum[now];
long long t1=0,t2=0; int m=(L+R)>>1;
if(x<=m)t1=getsum(chi[now][0],L,m,x,y);
if(y>m)t2=getsum(chi[now][1],m+1,R,x,y);
return t1+t2;
}
long long _A[maxn*2],_B[maxn*2];
void pushtag(int now,int L,int R,long long A,long long B)
{
if(!tag[now]){ tag[now]=true; _A[now]=A; _B[now]=B ;}
else
{
if(A==_A[now])
{
_B[now]=min(_B[now],B);
}
else
{
if(A>_A[now]){swap(A,_A[now]);swap(B,_B[now]);}
long long T=(B-_B[now])/(_A[now]-A);
while(T*(_A[now]-A)<=(B-_B[now]))T++;
if(T<=d_first[now])
{
_A[now]=A; _B[now]=B;
}
else if(T>dsum[now])
{
}
else
{
if(T<=dsum[chi[now][0]])
{
pushtag(chi[now][0],L,(L+R)/2,_A[now],_B[now]);
_A[now]=A; _B[now]=B;
}
else
{
pushtag(chi[now][1],(L+R)/2+1,R,A,B+A*dsum[chi[now][0]]);
}
}
}
}
return ;
}
long long update(int now,int L,int R,int x,int y,long long A,long long B)
{
if(x<=L && R<=y)
{
pushtag(now,L,R,A,B);
minv[now]=min(minv[now],A>=0 ? A*d_first[now]+B : A*dsum[now]+B);
return dsum[now];
}
int m=(L+R)>>1; long long tmp=0;
if(x<=m)tmp=update(chi[now][0],L,m,x,y,A,B);
if(y>m)tmp=tmp+update(chi[now][1],m+1,R,x,y,A,B+tmp*A);
minv[now]=min(minv[now],min(minv[chi[now][0]],minv[chi[now][1]]));
return tmp;
}
long long get(int now,int L,int R,int x)
{
if(!tag[now])return oo;
else return _A[now]*getsum(rt,1,n,L,x)+_B[now];
}
long long query(int now,int L,int R,int x,int y)
{
long long ans=oo;
if(tag[now] && x<=R && y>=L)ans=min(ans,_A[now]>=0? get(now,L,R,max(x,L)):get(now,L,R,min(y,R)));
if(x<=L && R<=y)
{
return min(ans,minv[now]);
}
int m=(L+R)>>1;
if(x<=m)ans=min(ans,query(chi[now][0],L,m,x,y));
if(y>m)ans=min(ans,query(chi[now][1],m+1,R,x,y));
return ans;
}
void op1(int x,int y,long long a,long long b)
{
int z=LCA(x,y);
long long dissum=0;
while(tp[x]!=tp[z])
{
dissum+=getsum(rt,1,n,tr[tp[x]],tr[x]);
update(rt,1,n,tr[tp[x]],tr[x],-a,dissum*a+b);
x=f[tp[x]];
}
dissum+=getsum(rt,1,n,tr[z],tr[x]);
update(rt,1,n,tr[z],tr[x],-a,dissum*a+b);
dissum-=fd[z];
dissum+=dis[y]-dis[z];
while(tp[y]!=tp[z])
{
dissum-=getsum(rt,1,n,tr[tp[y]],tr[y]);
update(rt,1,n,tr[tp[y]],tr[y],a,dissum*a+b);
y=f[tp[y]];
}
if(y!=z){dissum-=getsum(rt,1,n,tr[son[z]],tr[y]);update(rt,1,n,tr[son[z]],tr[y],a,dissum*a+b);}
return ;
}
char s[35];int cc;
void out(long long x)
{
if(!x)putchar('0');
if(x<0){x=-x;putchar('-');}
cc=0; while(x){s[++cc]=x%10+'0'; x=x/10;}
while(cc){putchar(s[cc]);cc--;}
putchar('\n');
return ;
}
void op2(int x,int y)
{
int z=LCA(x,y);
long long ans=oo;
while(tp[x]!=tp[z])
{
ans=min(ans,query(rt,1,n,tr[tp[x]],tr[x])); x=f[tp[x]];
}
ans=min(ans,query(rt,1,n,tr[z],tr[x]));
while(tp[y]!=tp[z])
{
ans=min(ans,query(rt,1,n,tr[tp[y]],tr[y])); y=f[tp[y]];
}
ans=min(ans,query(rt,1,n,tr[z],tr[y]));
out(ans);
return ;
}
void work()
{
for(int i=1;i<=n;i++)_tr[tr[i]]=i;
build(rt,1,n);
char op;int s,t;long long a,b;
int cct=0;
for(int i=1;i<=m;i++)
{
op=getchar(); while(op!='1'&&op!='2')op=getchar();
if(op=='1')
{
if(i==2458)
{
int hh;
hh++;
}
_read(s); _read(t); _readLL(a); _readLL(b); op1(s,t,a,b);
}
else
{
cct++;
if(i==2459)
{
int hh;
hh++;
}
_read(s); _read(t);/* s=110;t=110;printf("%d ",i);*/op2(s,t);
}
}
return ;
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
Init();
work();
return 0;
}