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;
}