[CF 932F][李超线段树]Escape Through Leaf

本文介绍了一种利用李超线段树优化动态规划算法的方法,通过启发式合并策略,有效地处理了多条直线的最优选择问题,实现复杂度优化。文章详细解释了如何在动态规划中应用李超线段树,并提供了具体的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考https://www.cnblogs.com/zjp-shadow/p/9180474.html

照那样推出dp方程,可以套用李超线段树。

问题的关键是如何合并两棵李超线段树。

采用启发式合并。

m条直线的李超线段树最多m个节点

我们遍历较小的李超线段树上的每个节点上的优势直线,加入另一棵李超线段树,分析一下发现复杂度会是对的,两个log,van♂了。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int lim=100000;
const ll All=1e15;
int n,m;
#define Maxn 100010
#define V 2000010
int head[Maxn],v[Maxn<<1],nxt[Maxn<<1],tot=0;
int a[Maxn],b[Maxn];
ll dp[Maxn];
inline void add_edge(int s,int e){
    tot++;v[tot]=e;nxt[tot]=head[s];head[s]=tot;
    tot++;v[tot]=s;nxt[tot]=head[e];head[e]=tot;
}
struct Line{
    int k;
    ll b;
    inline ll y(int x){return 1ll*k*x+b;}
};
struct Node{
    Line ans;
    int son[2]; 
    int siz;
}tree[V];
int root[Maxn],cnt=0;
void Insert(int &k,int l,int r,Line pos){
    if(!k){
        k=++cnt;
        tree[k].ans=pos;
        tree[k].siz=1;
        return;
    }
    ll p1=tree[k].ans.y(l),p2=tree[k].ans.y(r);
    p1=-p1;p2=-p2;
    ll c1=pos.y(l),c2=pos.y(r);
    c1=-c1;c2=-c2;
    if(c1>p1&&c2>p2){
        tree[k].ans=pos;
        return;
    }
    if(c1<=p1&&c2<=p2)return;
    double at=1.0*(tree[k].ans.b-pos.b)/(pos.k-tree[k].ans.k);
    int mid=(l+r)>>1;
    if(c1>p1){
        if(at>mid){
            Insert(tree[k].son[1],mid+1,r,tree[k].ans);
            tree[k].ans=pos;
        }else Insert(tree[k].son[0],l,mid,pos);
    }else{
        if(at>mid)Insert(tree[k].son[1],mid+1,r,pos);
        else{
           Insert(tree[k].son[0],l,mid,tree[k].ans);
           tree[k].ans=pos;
        }
    }
    tree[k].siz=tree[tree[k].son[0]].siz+tree[tree[k].son[1]].siz+1;
}
void merge(int &k,int u,int l,int r){
    if(!u)return;
    Insert(k,l,r,tree[u].ans);
    int mid=(l+r)>>1;
    if(tree[u].son[0])merge(tree[k].son[0],tree[u].son[0],l,mid);
    if(tree[u].son[1])merge(tree[k].son[1],tree[u].son[1],mid+1,r);
}
ll Query(int k,int l,int r,int pos){
    if(!k)return All;
    ll res=tree[k].ans.y(pos);
    int mid=(l+r)>>1;
    if(l==r)return res;
    if(pos<=mid)return min(res,Query(tree[k].son[0],l,mid,pos));
    else return min(res,Query(tree[k].son[1],mid+1,r,pos));
}
int seq[Maxn],dfk=0; 
void dfs(int u,int f){
    seq[++dfk]=u;
    for(int i=head[u];i;i=nxt[i])
        if(v[i]^f){
            dfs(v[i],u);
            if(tree[root[u]].siz<tree[root[v[i]]].siz)swap(root[u],root[v[i]]);
            merge(root[u],root[v[i]],0,2*lim);
    }
    if(!root[u])dp[u]=0;
    else dp[u]=Query(root[u],0,2*lim,a[u]+lim);
    Insert(root[u],0,2*lim,(Line){b[u],dp[u]-1ll*b[u]*lim});
}
  
inline void rd(int &x){
    x=0;char ch=getchar();int f=1;
    while(ch<'0'||ch>'9'){
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    x*=f;
}
  
int main(){
    rd(n);
    for(register int i=1;i<=n;++i)rd(a[i]);
    for(register int i=1;i<=n;++i)rd(b[i]);
    int s,e;
    for(register int i=1;i<n;++i){
        rd(s);rd(e);
        add_edge(s,e);
    }
    dfs(1,0);
    for(register int i=1;i<=n;++i)printf("%lld ",dp[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值