P4069 [SDOI2016] 游戏

题意

一棵 \(n\) 个点的带权树,边有边权,每个点也有一个权值,初始为 \(123456789123456789\)。令 \(dis(u,v)\) 为树上 \(u,v\) 之间简单路径上的边权和。两种操作:

  1. 给出 \(s,t,a,b\),对于 \(s,t\) 路径上的点 \(u\),将 \(u\) 的权值和 \(a\times dis(s,u)+b\)\(\min\)
  2. 给出 \(s,t\),求 \(s,t\) 路径上的点权最小值。

\(n,m\le10^5\),其中 \(m\) 为操作次数。

思路

直接用树剖+李超线段树解决。
时间复杂度 \(\mathcal O(n\log^3n)\),跑得飞快。

代码

// Problem: P4069 [SDOI2016] 游戏
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P4069
// Memory Limit: 250 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T&x){
        x=0;char c=getchar();bool f=0;
        while(!isdigit(c)) c=='-'?f=1:0,c=getchar();
        while(isdigit(c)) x=x*10+c-'0',c=getchar();
        f?x=-x:0;
    }
    template<typename T>
    inline void write(T x){
        if(x==0){putchar('0');return ;}
        x<0?x=-x,putchar('-'):0;short st[50],top=0;
        while(x) st[++top]=x%10,x/=10;
        while(top) putchar(st[top--]+'0');
    }
    inline void read(char&c){c=getchar();while(isspace(c)) c=getchar();}
    inline void write(char c){putchar(c);}
    inline void read(string&s){s.clear();char c;read(c);while(!isspace(c)&&~c) s+=c,c=getchar();}
    inline void write(string s){for(int i=0,len=s.size();i<len;i++) putchar(s[i]);}
    template<typename T>inline void write(T*x){while(*x) putchar(*(x++));}
    template<typename T,typename...T2> inline void read(T&x,T2&...y){read(x),read(y...);}
    template<typename T,typename...T2> inline void write(const T x,const T2...y){write(x),putchar(' '),write(y...),sizeof...(y)==1?putchar('\n'):0;}
}using namespace IO;
#define LL long long
const int maxn=100010;
const LL inf=1000000000000000000;
int n,m;
LL val[maxn],dis[maxn];
vector<pair<int,int>>e[maxn];
struct Line{//y=kx+b
    LL k,b;
    LL calc(LL x){return k*x+b;}
};
class Li_Chao_Segment_Tree{
private:
    struct node{Line x;LL minn;node(){minn=123456789123456789;x.b=123456789123456789;}}t[maxn*8];
    void update(int u,int l,int r,int ll,int rr,Line x){
        if(l>rr||r<ll) return ;
        int mid=l+r>>1;
        if(ll<=l&&r<=rr){
            if(x.calc(val[mid])<t[u].x.calc(val[mid])) swap(x,t[u].x);
            if(l==r){t[u].minn=t[u].x.calc(val[l]);return ;}
            if(t[u].x.calc(val[l])<x.calc(val[l])) update(u<<1|1,mid+1,r,ll,rr,x);
            else update(u<<1,l,mid,ll,rr,x);
            t[u].minn=min({t[u].x.calc(val[l]),t[u].x.calc(val[r]),t[u<<1].minn,t[u<<1|1].minn});
            return ;
        }
        update(u<<1,l,mid,ll,rr,x),update(u<<1|1,mid+1,r,ll,rr,x);
        t[u].minn=min({t[u].x.calc(val[l]),t[u].x.calc(val[r]),t[u<<1].minn,t[u<<1|1].minn});
    }
    LL query(int u,int l,int r,int ll,int rr){
        if(l>rr||r<ll) return inf;
        if(ll<=l&&r<=rr) return t[u].minn;
        int mid=l+r>>1;
        int lll=max(l,ll),rrr=min(r,rr);
        return min({query(u<<1,l,mid,ll,rr),query(u<<1|1,mid+1,r,ll,rr),t[u].x.calc(val[lll]),t[u].x.calc(val[rrr])});
    }
public:
    void update(int l,int r,Line x){update(1,1,n,l,r,x);}
    LL query(int l,int r){return query(1,1,n,l,r);}
}t;
namespace HLD{
    int fa[maxn],top[maxn],dfn[maxn],son[maxn],sz[maxn],deep[maxn],cntdfn;
    void dfs(int u,int f=0){
        fa[u]=f;deep[u]=deep[f]+1;
        sz[u]=1;
        for(auto[v,w]:e[u]){
            if(v==f) continue;
            dis[v]=dis[u]+w;
            dfs(v,u);
            sz[u]+=sz[v];
            if(sz[son[u]]<sz[v]) son[u]=v;
        }
    }
    void dfs2(int u,int tp){
        if(u==0) return ;
        dfn[u]=++cntdfn;top[u]=tp;
        dfs2(son[u],tp);
        for(auto[v,w]:e[u]){
            if(v==fa[u]) continue;
            if(v==son[u]) continue;
            dfs2(v,v);
        }
    }
    int lca(int u,int v){
        while(top[u]!=top[v]){
            if(deep[top[u]]<deep[top[v]]) swap(u,v);
            u=fa[top[u]];
        }
        if(deep[u]<deep[v]) swap(u,v);
        return v;
    }
    void update(int u,int v,Line x){
        while(top[u]!=top[v]){
            if(deep[top[u]]<deep[top[v]]) swap(u,v);
            t.update(dfn[top[u]],dfn[u],x);
            u=fa[top[u]];
        }
        if(deep[u]<deep[v]) swap(u,v);
        t.update(dfn[v],dfn[u],x);
    }
    LL query(int u,int v){
        LL ans=inf;
        while(top[u]!=top[v]){
            if(deep[top[u]]<deep[top[v]]) swap(u,v);
            ans=min(ans,t.query(dfn[top[u]],dfn[u]));
            u=fa[top[u]];
        }
        if(deep[u]<deep[v]) swap(u,v);
        return min(ans,t.query(dfn[v],dfn[u]));
    }
    void build(){
        dfs(1);
        dfs2(1,1);
        for(int i=1;i<=n;i++) val[dfn[i]]=dis[i];
    }
};
signed main(){
    read(n,m);
    for(int i=1;i<n;i++){
        int u,v,w;read(u,v,w);
        e[u].push_back({v,w});
        e[v].push_back({u,w});
    }
    Line x;
    HLD::build();
    for(int i=1;i<=m;i++){
        int op,s,t,a,b;read(op,s,t);
        if(op==1){
            read(a,b);
            int l=HLD::lca(s,t);
            x={-a,1ll*a*dis[s]+b};
            HLD::update(s,l,x);
            x={a,1ll*a*(dis[s]-2*dis[l])+b};
            HLD::update(l,t,x);
        }
        else write(HLD::query(s,t)),write("\n");
    }
    return 0;
}
posted @ 2026-08-10 15:29  Link-Cut_Trees  阅读(8)  评论(0)    收藏  举报