III.II.[SDOI2016]游戏

明显,一条从 \(x\)\(y\) 的路径可以被拆作两条从LCA下来的路径,并且路径上每个点被写上的数是关于其深度的一次函数。

于是就树剖套李超树就行了。

但是有个问题,李超树不是只支持单点询问吗,怎么这里又支持区间了呢?

我们发现,对于一条线段,其与我们询问区间可能有三种关系:相离,此时不需考虑;被完全包含于询问区间内,此时最小值一定在端点处取得,就直接用数据结构维护,插入线段时只处理两个端点,询问区间时询问所有被包含于其中的端点,无论是BIT还是常规线段树都可以轻松解决。

而剩下一种就是该线段不完全被包含于询问区间内,但依据单调性最优点一定处于端点位,所以就直接在李超树上询问端点处的最优答案即可。

复杂度 \(O(n\log^3n)\)。但是,李超树常数极小,配上树剖,仍然能搞过去。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll inf=123456789123456789ll;
int n,m,head[100100],cnt;
struct node{int to,next,val;}edge[200100];
void ae(int u,int v,int w){
	edge[cnt].next=head[u],edge[cnt].to=v,edge[cnt].val=w,head[u]=cnt++;
	edge[cnt].next=head[v],edge[cnt].to=u,edge[cnt].val=w,head[v]=cnt++;
}
int fa[100100],dep[100100],son[100100],sz[100100],dfn[100100],rev[100100],top[100100],tot;
ll dis[100100];
void dfs1(int x){
	sz[x]=1;
	for(int i=head[x];i!=-1;i=edge[i].next){
		if(edge[i].to==fa[x])continue;
		fa[edge[i].to]=x,dis[edge[i].to]=dis[x]+edge[i].val,dep[edge[i].to]=dep[x]+1;
		dfs1(edge[i].to);
		sz[x]+=sz[edge[i].to];
		if(sz[edge[i].to]>sz[son[x]])son[x]=edge[i].to;
	}
}
void dfs2(int x){
	dfn[x]=++tot,rev[tot]=x;if(!top[x])top[x]=x;
	if(son[x])top[son[x]]=top[x],dfs2(son[x]);
	for(int i=head[x];i!=-1;i=edge[i].next)if(edge[i].to!=fa[x]&&edge[i].to!=son[x])dfs2(edge[i].to);
}
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]];}if(dep[x]>dep[y])swap(x,y);return x;}
#define lson x<<1
#define rson x<<1|1
#define mid ((l+r)>>1)
namespace FS{//full section
	ll mn[400100];
	void build(int x,int l,int r){mn[x]=inf;if(l!=r)build(lson,l,mid),build(rson,mid+1,r);}
	void modify(int x,int l,int r,int P,ll val){if(l>P||r<P)return;mn[x]=min(mn[x],val);if(l!=r)modify(lson,l,mid,P,val),modify(rson,mid+1,r,P,val);}
	ll query(int x,int l,int r,int L,int R){if(l>R||r<L)return inf;if(L<=l&&r<=R)return mn[x];return min(query(lson,l,mid,L,R),query(rson,mid+1,r,L,R));}
}
namespace BS{//boundary situation
	struct SegTree{ll b;int k;}seg[400100];
	void build(int x,int l,int r){seg[x].k=0,seg[x].b=inf;if(l!=r)build(lson,l,mid),build(rson,mid+1,r);}
	void modify(int x,int l,int r,int L,int R,int K,ll B){
		if(l>R||r<L)return;
		if(L<=l&&r<=R){
			if(dis[rev[mid]]*K+B<dis[rev[mid]]*seg[x].k+seg[x].b)swap(seg[x].k,K),swap(seg[x].b,B);
			if(dis[rev[l]]*K+B<dis[rev[l]]*seg[x].k+seg[x].b)modify(lson,l,mid,L,R,K,B);
			if(dis[rev[r]]*K+B<dis[rev[r]]*seg[x].k+seg[x].b)modify(rson,mid+1,r,L,R,K,B);
			return;
		}
		modify(lson,l,mid,L,R,K,B),modify(rson,mid+1,r,L,R,K,B);
	}
	ll query(int x,int l,int r,int P){
		if(l>P||r<P)return inf;
		ll ret=dis[rev[P]]*seg[x].k+seg[x].b;
		if(l!=r)ret=min(ret,query(lson,l,mid,P)),ret=min(ret,query(rson,mid+1,r,P));
		return ret;
	}
}
void modify(int L,int R,int K,ll B){L=dfn[L],R=dfn[R],FS::modify(1,1,n,L,dis[rev[L]]*K+B),FS::modify(1,1,n,R,dis[rev[R]]*K+B),BS::modify(1,1,n,L,R,K,B);}
ll query(int L,int R){L=dfn[L],R=dfn[R];return min(FS::query(1,1,n,L,R),min(BS::query(1,1,n,L),BS::query(1,1,n,R)));}
void chain(int x,int y,int K,ll B){while(top[x]!=top[y])modify(top[x],x,K,B),x=fa[top[x]];modify(y,x,K,B);}
void pathmodify(int x,int y,int A,int B){int z=LCA(x,y);chain(x,z,-A,dis[x]*A+B),chain(y,z,A,(dis[x]-dis[z]*2)*A+B);}
ll pathquery(int x,int y){
	ll ret=inf;
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		ret=min(ret,query(top[x],x)),x=fa[top[x]];
	}
	if(dep[x]>dep[y])swap(x,y);ret=min(ret,query(x,y));return ret;
}
int main(){
	scanf("%d%d",&n,&m),memset(head,-1,sizeof(head));
	for(int i=1,x,y,z;i<n;i++)scanf("%d%d%d",&x,&y,&z),ae(x,y,z);
	dfs1(1),dfs2(1);
//	for(int x=1;x<=n;x++)printf("FA:%d SN:%d SZ:%d DP:%d DS:%lld RV:%d DF:%d TP:%d\n",fa[x],son[x],sz[x],dep[x],dis[x],rev[x],dfn[x],top[x]);
	FS::build(1,1,n),BS::build(1,1,n);
	for(int i=1,x,y,a,b,tp;i<=m;i++){
		scanf("%d%d%d",&tp,&x,&y);
		if(tp==1)scanf("%d%d",&a,&b),pathmodify(x,y,a,b);
		else printf("%lld\n",pathquery(x,y));
	}
	return 0;
}