一、题目
二、解法
你看那个 a × d i s + b a\times dis+b a×dis+b 很像一次函数,可以用李超树维护。
这个 d i s dis dis 还是太抽象了,我们要把具体的函数表达式推出来才能做:
- 对于 ( u , l c a ) (u,lca) (u,lca) 这一条路径上的点,有: a × ( d i s [ u ] − d i s [ x ] ) + b = − a × d i s [ u ] + ( a d i s [ x ] + b ) a\times(dis[u]-dis[x])+b=-a\times dis[u]+(adis[x]+b) a×(dis[u]−dis[x])+b=−a×dis[u]+(adis[x]+b)
- 对于 ( v , l c a ) (v,lca) (v,lca) 这一条路径上的点,有: a × ( d i s [ x ] + d i s [ u ] − 2 d i s [ l c a ] ) + b = a × d i s [ x ] + a ( d i s [ u ] − d i s [ l c a ] ) + b a\times(dis[x]+dis[u]-2dis[lca])+b=a\times dis[x]+a(dis[u]-dis[lca])+b a×(dis[x]+dis[u]−2dis[lca])+b=a×dis[x]+a(dis[u]−dis[lca])+b
那么用树链剖分套李超树即可,这道题很特殊的一点李超树是区间查询的。
怎么办?我们维护一个 m n [ i ] mn[i] mn[i] 表示节点 i i i 管辖的范围的最小值,在 u p d a t a \tt updata updata 的时候 p u s h _ u p \tt push\_up push_up 维护就可以了,只用算左右端点处的值。如果区间没有完全覆盖,那么就查询 max ( l , L ) \max(l,L) max(l,L) 和 min ( r , R ) \min(r,R) min(r,R) 处的点值,其实把他当成标记永久化就很好理解了。
时间复杂度 O ( n log 3 n ) O(n\log^3n) O(nlog3n),难打,我们常数还是小啊, l u o g u \tt luogu luogu 直接 r a n k 4 \tt rank4 rank4
#include <cstdio>
#include <iostream>
using namespace std;
#define ll long long
const int M = 100005;
const ll inf = 123456789123456789ll;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,tot,f[M],dep[M],fa[M],son[M],siz[M];
int Index,num[M],top[M],id[M];ll mn[4*M],dis[M];
struct edge
{
int v,c,next;
edge(int V=0,int C=0,int N=0) : v(V) , c(C) , next(N) {}
}e[2*M];
struct node
{
ll k,b;
node(ll K=0,ll B=inf) : k(K) , b(B) {}
ll ask(ll x)
{
return k*dis[id[x]]+b;
}
}tr[4*M];
void dfs1(int u,int p)
{
fa[u]=p;siz[u]=1;
dep[u]=dep[p]+1;
for(int i=f[u];i;i=e[i].next)
{
int v=e[i].v,c=e[i].c;
if(v==p) continue;
dis[v]=dis[u]+c;
dfs1(v,u);
siz[u]+=siz[v];//忘打了
if(siz[son[u]]<siz[v])
son[u]=v;
}
}
void dfs2(int u,int tp)
{
top[u]=tp;
num[u]=++Index;
id[Index]=u;
if(son[u]) dfs2(son[u],tp);
for(int i=f[u];i;i=e[i].next)
if(e[i].v^fa[u] && e[i].v^son[u])
dfs2(e[i].v,e[i].v);
}
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]];
}
if(dep[u]>dep[v]) swap(u,v);
return u;
}
void up(int i)
{
mn[i]=min(mn[i],min(mn[i<<1],mn[i<<1|1]));
}
void ins(int i,int l,int r,int L,int R,node b)
{
if(L>r || l>R) return ;
int mid=(l+r)>>1;
if(L<=l && r<=R)
{
if(tr[i].ask(mid)>b.ask(mid))//b更优,下传tr[i]
{
if(tr[i].ask(l)<b.ask(l)) ins(i<<1,l,mid,L,R,tr[i]);
if(tr[i].ask(r)<b.ask(r)) ins(i<<1|1,mid+1,r,L,R,tr[i]);
tr[i]=b;
}
else
{
if(tr[i].ask(l)>b.ask(l)) ins(i<<1,l,mid,L,R,b);
if(tr[i].ask(r)>b.ask(r)) ins(i<<1|1,mid+1,r,L,R,b);
}
mn[i]=min(tr[i].ask(l),tr[i].ask(r));
if(l!=r) up(i);//NMB,一个臭b错误调nm一天
return ;
}
ins(i<<1,l,mid,L,R,b);
ins(i<<1|1,mid+1,r,L,R,b);
up(i);
}
ll ask(int i,int l,int r,int L,int R)
{
if(L>r || l>R) return inf;
if(L<=l && r<=R) return mn[i];
int mid=(l+r)>>1;ll Mn=min(tr[i].ask(max(l,L)),tr[i].ask(min(r,R)));
return min(Mn,min(ask(i<<1,l,mid,L,R),ask(i<<1|1,mid+1,r,L,R)));
}
void modify(int u,int v)
{
int a=read(),b=read();
node y1=node(-a,1ll*a*dis[u]+b);
node y2=node(a,a*(dis[u]-2*dis[lca(u,v)])+b);
while(top[u]^top[v])
{
if(dep[top[u]]>dep[top[v]])
{
ins(1,1,n,num[top[u]],num[u],y1);
u=fa[top[u]];
}
else
{
ins(1,1,n,num[top[v]],num[v],y2);
v=fa[top[v]];
}
}
if(dep[u]>dep[v]) ins(1,1,n,num[v],num[u],y1);
else ins(1,1,n,num[u],num[v],y2);
}
ll query(int u,int v)
{
ll res=inf;
while(top[u]^top[v])
{
if(dep[top[u]]<dep[top[v]]) swap(u,v);
res=min(res,ask(1,1,n,num[top[u]],num[u]));
u=fa[top[u]];
}
if(dep[u]>dep[v]) swap(u,v);
res=min(res,ask(1,1,n,num[u],num[v]));
return res;
}
signed main()
{
n=read();m=read();
for(int i=1;i<n;i++)
{
int u=read(),v=read(),c=read();
e[++tot]=edge(v,c,f[u]),f[u]=tot;
e[++tot]=edge(u,c,f[v]),f[v]=tot;
}
dfs1(1,0);
dfs2(1,0);
for(int i=1;i<4*M;i++) mn[i]=inf;
while(m--)
{
int op=read(),s=read(),t=read();
if(op==1) modify(s,t);
else printf("%lld\n",query(s,t));
}
}