题目概述
给出
n
个点的树,
-
s t a b
:在
s→t
上的每个点
i
都插入一个数
a×dis(s,i)+b 。 - s t :询问 s→t 中最小的数。
解题报告
显然用李超线段树(查了四天了QAQ),由于在树上,所以先树剖。对于 s→t ,我们的想法肯定是拆成两半:
- s→lca : a×(dep[s]−dep[i])+b=−a×dep[i]+b+a×dep[s]
- lca→t : a×(dep[i]−dep[lca]+dep[s]−dep[lca])+b=a×dep[i]+b−a×(2dep[lca]−dep[s])
然后把线段树下标
x
换成
等等,我们会发现一个问题,就是新下标不是递增的!这会导致无法快速求出一次函数极值!
实际上由于树剖之后只会询问重链,而重链中的 dep 是递增的,所以没有问题。
解题报告
#include<cstdio>
#include<cctype>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long LL;typedef long double DB;
const int maxn=100000,maxm=maxn<<1;const LL INF=123456789123456789;
int n,te,tot,ID[maxn+5],who[maxn+5];
int E,lnk[maxn+5],nxt[maxm+5],son[maxm+5],w[maxm+5];
int si[maxn+5],SH[maxn+5],fa[maxn+5],top[maxn+5];LL dep[maxn+5];
struct Line {LL k,b;bool ID;Line(LL A=0,LL B=0,bool C=0) {k=A;b=B;ID=C;}};
Line tr[(maxn<<2)+5];LL MIN[(maxn<<2)+5];
#define Eoln(x) ((x)==10||(x)==13||(x)==EOF)
inline char readc()
{
static char buf[100000],*l=buf,*r=buf;
if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
if (l==r) return EOF;return *l++;
}
inline int readi(int &x)
{
int tot=0,f=1;char ch=readc(),lst='+';
while (!isdigit(ch)) {if (ch==EOF) return EOF;lst=ch;ch=readc();}
if (lst=='-') f=-f;
while (isdigit(ch)) tot=(tot<<3)+(tot<<1)+ch-48,ch=readc();
return x=tot*f,Eoln(ch);
}
void Dfs(int x,int pre=0)
{
fa[x]=pre;si[x]=1;
for (int j=lnk[x];j;j=nxt[j]) if (son[j]!=pre)
{
dep[son[j]]=dep[x]+w[j];Dfs(son[j],x);si[x]+=si[son[j]];
if (si[son[j]]>si[SH[x]]) SH[x]=son[j];
}
}
void HLD(int x,int pre=0,int lst=1)
{
ID[x]=++tot;who[tot]=x;top[x]=lst;if (SH[x]) HLD(SH[x],x,lst);
for (int j=lnk[x];j;j=nxt[j]) if (son[j]!=pre)
if (son[j]!=SH[x]) HLD(son[j],x,son[j]);
}
#define LS (p<<1)
#define RS (p<<1|1)
#define Y(l,x) (l.k*(x)+l.b)
#define cmp(a,b,pos) (a.ID&&Y(a,pos)<Y(b,pos))
#define X(p) (dep[who[p]])
inline void Pushup(int l,int r,int p)
{
if (l<r) MIN[p]=min(MIN[LS],MIN[RS]); else MIN[p]=INF;
if (tr[p].ID) MIN[p]=min(MIN[p],min(Y(tr[p],X(l)),Y(tr[p],X(r))));
}
void Pushdown(Line x,int l,int r,int p)
{
if (!tr[p].ID) tr[p]=x;if (cmp(x,tr[p],X(l))) swap(x,tr[p]);
if (l==r||x.k==tr[p].k) return Pushup(l,r,p);DB pos=(DB)(tr[p].b-x.b)/(x.k-tr[p].k);
if (pos<X(l)||X(r)<pos) return Pushup(l,r,p);int mid=l+(r-l>>1);
if (pos<=X(mid)) Pushdown(tr[p],l,mid,LS),tr[p]=x; else Pushdown(x,mid+1,r,RS);
Pushup(l,r,p);
}
void Insert(int L,int R,Line x,int l=1,int r=n,int p=1)
{
if (R<l||r<L) return;if (L<=l&&r<=R) return Pushdown(x,l,r,p);
int mid=l+(r-l>>1);Insert(L,R,x,l,mid,LS);Insert(L,R,x,mid+1,r,RS);
Pushup(l,r,p);
}
LL Ask(int L,int R,int l=1,int r=n,int p=1)
{
if (R<l||r<L) return INF;if (L<=l&&r<=R) return MIN[p];LL now;
int mid=l+(r-l>>1);now=min(Ask(L,R,l,mid,LS),Ask(L,R,mid+1,r,RS));
if (!tr[p].ID) return now;L=max(L,l);R=min(R,r);
return min(now,min(Y(tr[p],X(L)),Y(tr[p],X(R))));
}
inline 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]) return y;return x;
}
inline void Update(int x,int y,LL k,LL b)
{
while (top[x]!=top[y])
{
if (dep[top[x]]<dep[top[y]]) swap(x,y);
Insert(ID[top[x]],ID[x],Line(k,b,1));x=fa[top[x]];
}
if (dep[x]>dep[y]) swap(x,y);Insert(ID[x],ID[y],Line(k,b,1));
}
inline void Modify(int x,int y,LL k,LL b)
{
int lca=LCA(x,y);Update(x,lca,-k,b+k*dep[x]);
Update(lca,y,k,b-k*((dep[lca]<<1)-dep[x]));
}
inline LL Query(int x,int y)
{
LL MIN=INF;
while (top[x]!=top[y])
{
if (dep[top[x]]<dep[top[y]]) swap(x,y);
MIN=min(MIN,Ask(ID[top[x]],ID[x]));x=fa[top[x]];
}
if (dep[x]>dep[y]) swap(x,y);return min(MIN,Ask(ID[x],ID[y]));
}
#define Add(x,y,z) son[++E]=(y),w[E]=(z),nxt[E]=lnk[x],lnk[x]=E
int main()
{
freopen("program.in","r",stdin);
freopen("program.out","w",stdout);
readi(n);readi(te);for (int i=1;i<=(n<<2);i++) MIN[i]=INF;
for (int i=1,x,y,z;i<n;i++)
readi(x),readi(y),readi(z),Add(x,y,z),Add(y,x,z);
for (Dfs(1),HLD(1);te;te--)
{
int td,s,t,a,b;readi(td);readi(s);readi(t);
if (td==2) printf("%lld\n",Query(s,t)); else
readi(a),readi(b),Modify(s,t,a,b);
}
return 0;
}