【树链剖分+李超线段树】BZOJ4515(Sdoi2016)[游戏]题解

题目概述

给出 n 个点的树, m 个操作,操作有两种:

  1. s t a b :在 st 上的每个点 i 都插入一个数 a×dis(s,i)+b
  2. s t :询问 st 中最小的数。

解题报告

显然用李超线段树(查了四天了QAQ),由于在树上,所以先树剖。对于 st ,我们的想法肯定是拆成两半:

  • slca a×(dep[s]dep[i])+b=a×dep[i]+b+a×dep[s]
  • lcat a×(dep[i]dep[lca]+dep[s]dep[lca])+b=a×dep[i]+ba×(2dep[lca]dep[s])

然后把线段树下标 x 换成 x 对应的 dep 就行了(tm就是因为有个地方没换,查了半天)。

等等,我们会发现一个问题,就是新下标不是递增的!这会导致无法快速求出一次函数极值!

实际上由于树剖之后只会询问重链,而重链中的 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值