http://www.lydsy.com/JudgeOnline/problem.php?id=3672

 

法一:线段树维护可持久化单调队列维护凸包 斜率优化DP

设dp[i] 表示i号点到根节点的最少花费

dis[i] 表示 点i到根节点的距离

dp[i]= min { (dis[i]-dis[j])* P[i] + Q[i] + dp[j] }   j是i的祖先且dis[i]-dis[j]<=L[i]

即 dp[i]+dis[j]*P[i]=dp[j]+dis[i]*P[i]+Q[i]

斜率优化,dp[i]=斜率为P[i] 的直线 截(dis[j],dp[j])所得的截距最小值

如果不考虑L[i] 的限制:

那就可以用一个可持久化的单调队列维护 根节点到当前点的路径上所有的点构成的下凸壳

 

考虑如何去除兄弟节点的子树对单调队列的影响

 

即在一个节点退出dfs时,将单调队列恢复为这个节点开始dfs的情况

头指针:头指针的前移不会涉及单调队列元素的修改,因为从根往下走不能保证斜率单调,所以头指针不能出队,需要二分找到最小的 斜率>P[i] 的点来更新dp[i]

尾指针:尾指针的前移会涉及到单调队列元素的替换,但替换只会替换一个元素,所以记录下 替换的是谁,原来的尾指针是谁,退出的时候恢复即可

加上L[i]的限制:

用线段树维护,若线段树节点的区间为[l,r],那这个节点维护的是当前链上  深度为[l,r]的点构成的凸包

即由原来的维护一个单调队列变成维护nlogn个单调队列

每个节点开一个vector? 光开nlogn个vector就TLE了吧

开一个nlogn的数组v存储整条链上凸包内的点

在建树的时候,按节点的顺序给头指针分一个位置x,若节点的大小为y,

那么这个节点维护的单调队列 中的点会出现在v数组的[x,x+y-1]位置

这样线段树内只需要存储单调队列的头尾指针即可

 

单调队列采用 左闭右开 的好处:

多个可持久化单调队列,头尾指针相连

如果头尾指针是左闭右闭

如果队列为空,tail<head,导致 后一个队列的尾指针=前一个队列的头指针

这样尾指针替换更改,记录的替换位置为tail,即前一个队列的head

退出dfs替换回去 错误的替换了前一个队列的head

头尾指针左闭右开就不会有这个问题

 

#include<cstdio>
#include<iostream>
#include<algorithm>
 
using namespace std;
 
#define N 200001
 
typedef long long LL;
 
int n;
int P[N];
LL Q[N],L[N];
 
int front[N],to[N],nxt[N],tot;
LL val[N];
 
struct node
{
    int head,tail;
}tr[N<<2];
int v[N*19];
 
int sum;
 
LL dep[N],dis[N];
LL dp[N];
 
int re_t[N][19],re_w[N][19];
 
template<typename T>
void read(T &x)
{
    x=0; char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
}
 
void add(int u,int v,LL w)
{
    to[++tot]=v; nxt[tot]=front[u]; front[u]=tot; val[tot]=w;
}
 
void build(int k,int l,int r)
{
    tr[k].head=sum+1;
    tr[k].tail=tr[k].head;
    sum+=r-l+1;
    if(l==r) return;
    int mid=l+r>>1;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
}
 
inline double slope(int i,int j)
{
    return 1.0*(dp[j]-dp[i])/(dis[j]-dis[i]);
}
 
int find_ans(int k,int p)
{
    int l=tr[k].head;
    int r=tr[k].tail-1;
    if(l==r) return v[l];
    r--;
    int mid,tmp=-1;
    while(l<=r)
    {
        mid=l+r>>1;
        if(p<slope(v[mid],v[mid+1])) tmp=mid,r=mid-1;
        else l=mid+1;
    }
    if(tmp==-1) return v[tr[k].tail-1];
    return v[tmp];
}   
 
int query(int k,int l,int r,int opl,int opr,int p)
{
    if(l>=opl && r<=opr) return find_ans(k,p);
    int mid=l+r>>1;
    if(opr<=mid) return query(k<<1,l,mid,opl,opr,p);
    else if(opl>mid) return query(k<<1|1,mid+1,r,opl,opr,p);
    else
    {
        int t1=query(k<<1,l,mid,opl,opr,p);
        int t2=query(k<<1|1,mid+1,r,opl,opr,p);
        if(p<slope(t1,t2)) return t1;
        return t2;
    }
}   
 
int find(int r,LL lim)
{
    int l=1;
    int tmp,mid;
    while(l<=r)
    {
        mid=l+r>>1;
        if(dep[mid]>=lim) tmp=mid,r=mid-1;
        else l=mid+1;
    }
    return tmp;
}
 
int in_queue(int k,int j)
{
    if(tr[k].head==tr[k].tail) return tr[k].head;
    int l=tr[k].head,r=tr[k].tail-2,tmp=-1,mid;
    while(l<=r)
    {
        mid=l+r>>1;
        if(slope(v[mid],v[mid+1])>slope(v[mid],j)) tmp=mid+1,r=mid-1;
        else l=mid+1;
    }
    if(tmp==-1) return tr[k].tail;
    return tmp;
}
 
void insert(int k,int l,int r,int x,int w,int d)
{
    int pos=in_queue(k,w);
    re_t[w][d]=tr[k].tail;
    re_w[w][d]=v[pos];
    v[pos]=w;
    tr[k].tail=pos+1;
    if(l==r) return;
    int mid=l+r>>1;
    if(x<=mid) insert(k<<1,l,mid,x,w,d+1);
    else insert(k<<1|1,mid+1,r,x,w,d+1);
}
 
void del(int k,int l,int r,int x,int w,int d)
{
    v[tr[k].tail-1]=re_w[w][d];
    tr[k].tail=re_t[w][d];
    if(l==r) return;
    int mid=l+r>>1;
    if(x<=mid) del(k<<1,l,mid,x,w,d+1);
    else del(k<<1|1,mid+1,r,x,w,d+1);
}
 
void dfs(int x,int d)
{
    if(x!=1)
    {
        LL lim=dis[x]-L[x];
        int j=query(1,1,n,find(d-1,lim),d-1,P[x]);  
        dp[x]=(dis[x]-dis[j])*P[x]+Q[x]+dp[j];
    }
    insert(1,1,n,d,x,1);
    for(int i=front[x];i;i=nxt[i])
    {
        dis[to[i]]=dis[x]+val[i];
        dep[d+1]=dep[d]+val[i];
        dfs(to[i],d+1);
    }
    del(1,1,n,d,x,1);
}
 
int main()
{
    int t;
    read(n); read(t);
    int f; LL s;
    for(int i=2;i<=n;++i)
    {
        read(f); read(s); read(P[i]); read(Q[i]); read(L[i]);
        add(f,i,s);
    }
    sum=0; build(1,1,n);
    insert(1,1,n,1,1,1);
    dfs(1,1);
    for(int i=2;i<=n;++i) cout<<dp[i]<<'\n';
    return 0;
}
View Code

相关文章: