传送门
仔细一看:每个节点动态维护半平面交??
一搜,原来是李超线段树。
把原树剖分一下,就转化为二维平面内如下操作:
①加上一条线段。
②询问一段区间内的最小值。
该线段树利用了标记永久化的思想。
每个区间
[
l
,
r
]
[l,r]
[l,r]只保留一条线段:
k
∗
m
i
d
+
b
k*mid+b
k∗mid+b最小的那条。也就是与直线
x
=
m
i
d
x=mid
x=mid相交的最下面的一条。插入线段的时候根据交点维护这个东西即可:
①插入线段全部在当前节点最优线段上方,就一定没有贡献。
②插入线段全部在当前节点最优线段下方,就把当前节点最优线段换掉然后返回。因为不可能在子区间里使得之前的最优线段比当前插入线段更优,所以在询问的时候插入线段完全取代了之前的最优线段。
③不满足①,②就一定有交点。在继续下面的操作之前,先考虑当前最优线段是否可以被插入线段换掉(比较
m
i
d
mid
mid处函数值大小),可以的话就把两个线段交换,然后根据交点的位置递归下去插入到子区间里。这样复杂度是
O
(
l
o
g
n
2
)
O(log n^2)
O(logn2)的。
其实就相当于是在这个半平面交中,每个外边缘的线段都找到了一个对应点。 这些边缘线段的贡献就由这些节点来存储。
由于要求区间最小,又由于一次函数的单调性,所以最小值一定在端点处取得。所以每个节点根据它中点处存储的“最优线段”可以得到一个最小值,每次插入后记得 p u s h u p pushup pushup一下就行了。
在询问区间最小的时候,当前节点的区间范围有可能会大于询问范围,所以每个节点的函数值要重新根据询问范围算一下。
由于最开始每个点都有一个极大值,所以每个点初始化都有一条斜率为0,截距为 123456789123456789 123456789123456789 123456789123456789的直线,就不用打标记了。
#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const
using std::swap;
using std::min;
cs int N=1e5+10;
cs ll oo=123456789123456789ll;
namespace IO{
cs int Rlen=1<<22|1;
char buf[Rlen],*p1,*p2;
inline char gc(){return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;}
template<typename T>
inline T get(){
char ch=gc();T x=0,f=1;
while(!isdigit(ch)){if(ch=='-') f=-1;ch=gc();}
while(isdigit(ch)) x=((x+(x<<2))<<1)+(ch^48),ch=gc();
return x*f;
}
inline int gi(){return get<int>();}
inline ll gl(){return get<ll>();}
}
using IO::gi;
using IO::gl;
int n,m,u,v,g,op;ll a,b,w;
inline void Min(ll &a,ll b){if(a>b)a=b;}
int Head[N],Next[N<<1],V[N<<1],cnt=0;ll W[N<<1],dis[N];
int dep[N],fa[N],son[N],siz[N],top[N],dfn[N],t[N],tot=0;
namespace SGT{
#define lc (root<<1)
#define rc (root<<1|1)
#define mid (T[root].l+T[root].r>>1)
struct Line{
ll K,B;
Line(ll k=0,ll b=oo){K=k,B=b;}
inline ll val(ll x){return K*x+B;}
};
inline double cross(cs Line &a,cs Line &b){return (double)(a.B-b.B)/(double)(b.K-a.K);}
struct node{
int l,r;ll ans;Line L;
node(){ans=oo;}
}T[N<<2];
inline void pushup(int root){
if(T[root].l<T[root].r) Min(T[root].ans,T[lc].ans),Min(T[root].ans,T[rc].ans);
Min(T[root].ans,T[root].L.val(dis[t[T[root].l]])),
Min(T[root].ans,T[root].L.val(dis[t[T[root].r]]));
}
inline void build(int root,int l,int r){
T[root].l=l,T[root].r=r;if(l==r) return;
build(lc,l,mid),build(rc,mid+1,r);
}
inline void update(int root,Line L){
ll yl_1=T[root].L.val(dis[t[T[root].l]]),yr_1=T[root].L.val(dis[t[T[root].r]]);
ll yl_2=L.val(dis[t[T[root].l]]),yr_2=L.val(dis[t[T[root].r]]);
if(yl_1<=yl_2 && yr_1<=yr_2) return;
if(yl_1> yl_2 && yr_1> yr_2){T[root].L=L;return pushup(root);}
if(L.val(dis[t[mid]])<T[root].L.val(dis[t[mid]])) swap(L,T[root].L);
double pos=cross(T[root].L,L);
(pos<=dis[t[mid]])?update(lc,L):update(rc,L);
return pushup(root);
}
inline void insert(int root,int l,int r,cs Line &L){
if(l<=T[root].l&&T[root].r<=r) return update(root,L);
if(l> mid) return insert(rc,l,r,L),pushup(root);
if(r<=mid) return insert(lc,l,r,L),pushup(root);
insert(lc,l,r,L),insert(rc,l,r,L),pushup(root);
}
inline ll query(int root,int l,int r,ll ret=oo){
if(l<=T[root].l&&T[root].r<=r) return T[root].ans;
Min(ret,T[root].L.val(dis[t[l]])),
Min(ret,T[root].L.val(dis[t[r]]));
if(r<=mid){Min(ret,query(lc,l,r));return ret;}
if(l> mid){Min(ret,query(rc,l,r));return ret;}
Min(ret,min(query(lc,l,mid),query(rc,mid+1,r)));return ret;
}
}
using namespace SGT;
inline void add(int u,int v,ll w){Next[++cnt]=Head[u],V[cnt]=v,W[cnt]=w,Head[u]=cnt;}
void dfs1(int u,int f){
siz[u]=1,son[u]=0,dep[u]=dep[f]+1,fa[u]=f;
for(int re i=Head[u],v=V[i];i;v=V[i=Next[i]])if(v^f){
dis[v]=dis[u]+W[i],dfs1(v,u),siz[u]+=siz[v];
if(siz[son[u]]<siz[v]) son[u]=v;
}
}
void dfs2(int u,int tp){
t[dfn[u]=++tot]=u,top[u]=tp;
if(son[u]) dfs2(son[u],tp);
for(int re i=Head[u],v=V[i];i;v=V[i=Next[i]])
if((v^fa[u])&&(v^son[u])) dfs2(v,v);
}
inline int lca(int u,int v){
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
u=fa[top[u]];
}return dep[u]<dep[v]?u:v;
}
inline void modify(int u,int v,int g,ll a,ll b,bool flag=0){
int U=u,V=v;
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v),flag^=1;
if(!flag) insert(1,dfn[top[u]],dfn[u],Line(-a,a*dis[U]+b));
else insert(1,dfn[top[u]],dfn[u],Line(a,a*(dis[U]-dis[g]*2)+b));
u=fa[top[u]];
}if(dep[u]<dep[v]) swap(u,v),flag^=1;
if(!flag) insert(1,dfn[v],dfn[u],Line(-a,a*dis[U]+b));
else insert(1,dfn[v],dfn[u],Line(a,a*(dis[U]-dis[g]*2)+b));
}
inline ll ask(int u,int v,ll ret=oo){
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
Min(ret,query(1,dfn[top[u]],dfn[u]));
u=fa[top[u]];
}if(dep[u]<dep[v]) swap(u,v);
Min(ret,query(1,dfn[v],dfn[u]));
return ret;
}
int main(){
// freopen("2561.in","r",stdin);
n=gi(),m=gi();
for(int re i=1;i< n;++i)
u=gi(),v=gi(),w=gl(),add(u,v,w),add(v,u,w);
dfs1(1,0),dfs2(1,1),build(1,1,n);
while(m--){
op=gi();
if(op==1) u=gi(),v=gi(),g=lca(u,v),a=gl(),b=gl(),modify(u,v,g,a,b);
if(op==2) u=gi(),v=gi(),printf("%lld\n",ask(u,v));
}
}