SDOI2016 题解
首先树剖,然后变成在 \(\text{dfn}\) 区间上插一个关于 \(\text{dis}\) 的一次函数。这个很神奇,一般的李超树是,在 \(x\) 轴区间上插入关于 \(x\) 的一次函数。然而这里,\(\text{dfn}\) 与 \(\text{dis}\) 看起来毫无关系。
考虑李超树运用了线段的什么性质。无外乎,单调性和线性性。
第一个显然,你树剖出来的 \(\text{dfn}\) 连续段是一条链,\(\text{dis}\) 随 \(\text{dfn}\) 单调递增,那关于 \(\text{dis}\) 单调的函数自然也关于 \(\text{dfn}\) 单调。所以李超树维护的实际上是一堆折线。
有人就说,你只是在某些区间满足这个单调性,整棵树维护的东西不是乱套?管你吗的整棵树,修改查询的所有区间都合法就能满足我的要求,靠近根的那些点根本查不到。
对于线性性,它实际上是在满足这个:
if(f(ln)<g(now)(ln)) dnf(ls(now),ln,mid,f);
if(f(rn)<g(now)(rn)) dnf(rs(now),mid+1,rn,f);
就是说,如果线段 \(f\) 的两端点分别高于 \(g\),那么 \(f\) 就没有任何一处的取值比 \(g\) 小。乍一看折线好像不行:
但是这里的折线不一般。注意到 \(\text{dfn}\) 到 \(\text{dis}\) 是一个映射,图中线段斜率 \(\text{dis}\times a\)(设关于 \(\text{dis}\) 函数的斜率是 \(a\)),那么如果折线 \(f\) 在某一处的斜率比 \(g\) 要大,\(f\) 所对应原函数的 \(a\) 也要更大,\(f\) 的斜率便永远大于 \(g\),上图相交两次的情况根本不存在。
然后注意一次函数求值要把对应的 \(\text{dis}\) 而不是 \(\text{dfn}\) 代进去就是了。
#include <cstdio>
#include <vector>
#include <algorithm>
#define ll long long
#define int long long
using namespace std;
const int N=114514;
const ll inf=123456789123456789ll;
ll dis[N];
struct lin{
int k;ll b;ll operator()(int x){return k*dis[x]+b;}
};
namespace sgt{
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
struct node{
lin g;ll mn;
#define g(x) t[x].g
#define mn(x) t[x].mn
}t[N<<2];
void updata(int x,int l,int r){
if(l!=r) mn(x)=min(mn(ls(x)),mn(rs(x)));
mn(x)=min(mn(x),min(g(x)(l),g(x)(r)));
}
void build(int now,int ln,int rn){
g(now).b=mn(now)=inf;
if(ln==rn) return ;
int mid=ln+rn>>1;
build(ls(now),ln,mid);
build(rs(now),mid+1,rn);
}
void dnf(int now,int ln,int rn,lin f){
if(g(now).b==inf) return g(now)=f,updata(now,ln,rn);
int mid=ln+rn>>1;
if(f(mid)<g(now)(mid)) swap(f,g(now));
if(ln==rn) return updata(now,ln,rn);
if(f(ln)<g(now)(ln)) dnf(ls(now),ln,mid,f);
if(f(rn)<g(now)(rn)) dnf(rs(now),mid+1,rn,f);
updata(now,ln,rn);
}
void upd(int now,int ln,int rn,int l,int r,lin f){
//printf("upd [%d %d] %d %d %dx+%d\n",ln,rn,l,r,f.k,f.b);
if(l<=ln&&rn<=r) return dnf(now,ln,rn,f);
int mid=ln+rn>>1;
if(l<=mid) upd(ls(now),ln,mid,l,r,f);
if(r>mid) upd(rs(now),mid+1,rn,l,r,f);
updata(now,ln,rn);
}
ll qry(int now,int ln,int rn,int l,int r){
//printf("qry [%d %d] %d %d\n",ln,rn,l,r);
if(l<=ln&&rn<=r) return /*printf("re res %lld\n",mn(now)),*/mn(now);
int mid=ln+rn>>1;
ll res=min(g(now)(max(ln,l)),g(now)(min(rn,r)));
//printf("res %lld\n",res);
if(l<=mid) res=min(res,qry(ls(now),ln,mid,l,r));
if(r>mid) res=min(res,qry(rs(now),mid+1,rn,l,r));
return res;
}
}
struct edg{int v,w;};
vector<edg> vc[N];
int fa[N],dep[N],siz[N],son[N];ll td[N];
void dfs1(int now,int lst){
fa[now]=lst;dep[now]=dep[lst]+1;siz[now]=1;
for(auto[v,w]:vc[now]) if(v!=lst){
td[v]=td[now]+w;dfs1(v,now);siz[now]+=siz[v];
if(siz[v]>siz[son[now]]) son[now]=v;
}
}
int top[N],dfn[N],dcnt;
void dfs2(int now,int tp){
top[now]=tp;dis[dfn[now]=++dcnt]=td[now];
if(son[now]) dfs2(son[now],tp);
for(auto[v,w]:vc[now]) if(!dfn[v]) dfs2(v,v);
}
struct itv{int l,r;}up[N],dw[N];int uc,dc,n;
void upd(int x,int y,int a,int b){
uc=dc=0;int s=dfn[x],c;
while(top[x]!=top[y]){
if(dep[top[x]]>dep[top[y]]) up[++uc]=(itv){dfn[top[x]],dfn[x]},x=fa[top[x]];
else dw[++dc]=(itv){dfn[top[y]],dfn[y]},y=fa[top[y]];
}
if(dep[x]>dep[y]) up[++uc]=(itv){dfn[y],dfn[x]},c=dfn[y];
else dw[++dc]=(itv){dfn[x],dfn[y]},c=dfn[x];
for(int i=1;i<=uc;++i) sgt::upd(1,1,n,up[i].l,up[i].r,(lin){-a,a*dis[s]+b});
for(int i=dc;i;--i) sgt::upd(1,1,n,dw[i].l,dw[i].r,(lin){a,a*(dis[s]-2*dis[c])+b});
}
ll qry(int x,int y){
ll res=inf;while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
res=min(res,sgt::qry(1,1,n,dfn[top[x]],dfn[x]));
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
return min(res,sgt::qry(1,1,n,dfn[x],dfn[y]));
}
signed main()
{
freopen("data.txt","r",stdin);
freopen("myass.txt","w",stdout);
int m,x,y,v,w;scanf("%lld%lld",&n,&m);
for(int i=1;i<n;++i) scanf("%lld%lld%lld",&x,&y,&w),
vc[x].push_back((edg){y,w}),vc[y].push_back((edg){x,w});
dfs1(1,0);dfs2(1,1);sgt::build(1,1,n);
//puts("Dfn");for(int i=1;i<=n;++i) printf("%d ",dfn[i]);puts("");
while(m--){int f;scanf("%lld%lld%lld",&f,&x,&y);switch(f){
case 1:scanf("%lld%lld",&v,&w);upd(x,y,v,w);break;
case 2:printf("%lld\n",qry(x,y));break;
}}
}