分析:
这道题要是让我做,就是模拟暴力
我们的目的是通过这道题学习一下超哥线段树
在树上的区间修改,维护最小值
可以立刻联想到树链剖分用线段树维护最小值
但是这个
dis
让人很不爽啊
显然s~t的路径可以分成s~lca和lca~t两条路径
令
deep[x]
表示
x
的深度,
对于s~lca上面的点
lca~t上面的点
x
的值相当于
这样就可以得到新的
a′
和
b′
,然后一个点的值就相当于
a′∗deep[x]+b′
了,
这样就只和
a′
,
b′
和
deep[x]
有关了
在树链剖分对应的线段树区间中,相连的一部分ta们在树中也是相连的,这样就满足
deep[]
单调递增,
观察
a′∗d[x]+b′
,发现这相当于一条直线
f(x)=a′x+b
这就相当于用线段树维护一个区间中的若干条直线在每个部分的最小值
这种数据结构就叫做:超哥线段树
常见的处理方式就是标记永久化(这里只是一定程度上的永久化但还是要下传的)
让线段树中的一个结点对应一条直线
那么如果在这个区间加入一条直线怎么办呢?
分类讨论
设新加入的
f1(x)=k1x+b1
,原来的
f2(x)=k2x+b2
左端点为
l
,右端点为
f1(deep[l])<f2(deep[l]) 且 f1(deep[r])<f2(deep[r])
对应一条直线在两个端点都比另一条小,那么显然在l~r中 f1(x) 处处比 f2(x) 小,直接把 f2(x) 替换为 f1(x)同理若上式的两个符号都为 > ,那么
f1(x) 处处不如 f2(x) 优,不做更改k1<k2 ,由于不满足1.2,显然两条直线有交点
此时解不等式 f1(x)<f2(x) 得到 x>(b1−b2)/(k2−k1) ,判断 (b1−b2)/(k2−k1) 在左半区间还是右半区间递归下传即可k1>k2 同理
询问就简单多了,直接用标记永久化的线段树的方法更新即可
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const ll INF=123456789123456789;
const int N=100005;
struct node{
int x,y,v,nxt;
};
node way[N<<1];
int st[N],tot=0;
struct Tree{
ll a,b,mn,sl,sr;
};
Tree T[N<<2];
ll deep[N],a,b;
int pre[N],top[N],n,m,num[N],shu[N],son[N],tt=0,sz[N],clo=0;
int x,y;
void add(int u,int w,int z)
{
tot++;
way[tot].x=u;way[tot].y=w;way[tot].v=z;way[tot].nxt=st[u];st[u]=tot;
tot++;
way[tot].x=w;way[tot].y=u;way[tot].v=z;way[tot].nxt=st[w];st[w]=tot;
}
void dfs_1(int now,int fa,int dep)
{
deep[now]=dep; pre[now]=fa;
sz[now]=1;
int maxx=0;
for (int i=st[now];i;i=way[i].nxt)
if (way[i].y!=fa)
{
dfs_1(way[i].y,now,dep+way[i].v);
sz[now]+=sz[way[i].y];
if (sz[way[i].y]>maxx)
{
maxx=sz[way[i].y];
son[now]=way[i].y;
}
}
}
void dfs_2(int now,int fa)
{
if (son[fa]!=now) top[now]=now;
else top[now]=top[fa];
num[now]=++clo; shu[num[now]]=now;
if (son[now])
{
dfs_2(son[now],now);
for (int i=st[now];i;i=way[i].nxt)
if (way[i].y!=fa&&way[i].y!=son[now])
dfs_2(way[i].y,now);
}
}
void build(int bh,int l,int r)
{
T[bh].mn=T[bh].b=INF;
if (l==r)
{
T[bh].sl=T[bh].sr=deep[shu[l]]; //l在树中的编号
return;
}
int mid=(l+r)>>1;
build(bh<<1,l,mid);
build((bh<<1)+1,mid+1,r);
T[bh].sl=T[bh<<1].sl;
T[bh].sr=T[(bh<<1)+1].sr;
}
int lca(int x,int y)
{
for (;top[x]!=top[y];x=pre[top[x]])
if (deep[top[x]]<deep[top[y]]) swap(x,y);
return (deep[x]<deep[y])? x:y;
}
void change(int bh,int l,int r,int L,int R,ll a,ll b)
{
int mid=(l+r)>>1;
if (l>=L&&r<=R)
{
ll z1=T[bh].sl*T[bh].a+T[bh].b; //左右端点的f值
ll z2=T[bh].sr*T[bh].a+T[bh].b;
ll z3=T[bh].sl*a+b;
ll z4=T[bh].sr*a+b;
if (z3<z1&&z4<z2)
T[bh].a=a,T[bh].b=b;
else if ((z3<z1&&z4>z2)||(z3>z1&&z4<z2))
change(bh<<1,l,mid,L,R,a,b),
change((bh<<1)+1,mid+1,r,L,R,a,b);
T[bh].mn=min(T[bh].mn,min(T[bh].sl*a+b,T[bh].sr*a+b)); //维护区间最小值
if (l!=r) T[bh].mn=min(T[bh].mn,min(T[bh<<1].mn,T[(bh<<1)+1].mn)); //update
return;
}
if (L<=mid) change(bh<<1,l,mid,L,R,a,b);
if (R>mid) change((bh<<1)+1,mid+1,r,L,R,a,b);
T[bh].mn=min(T[bh].mn,min(T[bh<<1].mn,T[(bh<<1)+1].mn));
return;
}
void modify()
{
scanf("%d%d%d%d",&x,&y,&a,&b);
ll c=deep[x]*a+b,d=(deep[x]-2*deep[lca(x,y)])*a+b;
ll ans=INF;
int f1=top[x],f2=top[y];
while (f1!=f2)
{
if (deep[f1]>deep[f2])
change(1,1,n,num[f1],num[x],-a,c),x=pre[f1]; //s~lca
else
change(1,1,n,num[f2],num[y],a,d),y=pre[f2]; //lca~t
f1=top[x];
f2=top[y];
}
if (deep[x]>deep[y])
change(1,1,n,num[y],num[x],-a,c);
else
change(1,1,n,num[x],num[y],a,d);
}
ll ask(int bh,int l,int r,int L,int R)
{
if (l>=L&&r<=R)
return T[bh].mn;
ll ans=min(deep[shu[L]]*T[bh].a+T[bh].b,deep[shu[R]]*T[bh].a+T[bh].b);
int mid=(l+r)>>1;
if (L<=mid) ans=min(ans,ask(bh<<1, l, mid,L, min(mid,R)));
if (R>mid) ans=min(ans,ask((bh<<1)+1,mid+1,r, max(mid+1,L),R));
return ans;
}
void askans()
{
scanf("%d%d",&x,&y);
ll ans=INF;
int f1=top[x],f2=top[y];
while (f1!=f2)
{
if (deep[f1]<deep[f2]) swap(x,y),swap(f1,f2);
ans=min(ans,ask(1,1,n,num[f1],num[x]));
x=pre[f1];
f1=top[x];
}
if (num[x]>num[y]) swap(x,y);
ans=min(ans,ask(1,1,n,num[x],num[y]));
printf("%lld\n",ans);
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<n;i++)
{
int u,w,z;
scanf("%d%d%d",&u,&w,&z);
add(u,w,z);
}
dfs_1(1,0,0); dfs_2(1,0);
build(1,1,n);
int opt;
for (int i=1;i<=m;i++)
{
scanf("%d",&opt);
if (opt==1) modify();
else askans();
}
return 0;
}