bzoj3672 [ NOI2014 ] -- 树上CDQ分治 + 斜率优化DP

8 篇文章 0 订阅
6 篇文章 0 订阅
神题
题解
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define N 200010
#define Eps 1e-15
#define ll long long
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void Read(int& x){
    char c=nc();
    for(;c<'0'||c>'9';c=nc());
    for(x=0;c>='0'&&c<='9';x=(x<<3)+(x<<1)+c-48,c=nc());
}
inline void Read(ll& x){
    char c=nc();
    for(;c<'0'||c>'9';c=nc());
    for(x=0;c>='0'&&c<='9';x=(x<<3)+(x<<1)+c-48,c=nc());
}
char ss[30];
int Len;
inline void Print(ll x){
    if(!x){
        puts("0");
        return;
    }
    for(Len=0;x;x/=10)ss[++Len]=x%10;
    while(Len)putchar(ss[Len--]+48);
    putchar('\n');
}
struct Edge{
    int t,nx;
    ll w;
}e[N];
struct Node{
    int f;ll w;
    Node(int f=0,ll w=0):f(f),w(w){}
}a[N];
int Rt,s[N],F[N],cnt,top,ch[N];
int i,j,n,m,p[N];
int x,y,f[N],num,h[N];
ll Ans[N],q[N],d[N],l[N],z;
bool b[N];
inline void Add(int x,int y,ll z){
    e[++num].t=y;e[num].w=z;e[num].nx=h[x];h[x]=num;
}
inline void Dfs1(int x){
    for(int i=h[x];i;i=e[i].nx)
    d[e[i].t]=d[x]+e[i].w,Dfs1(e[i].t);
}
inline int Max(int x,int y){
    return x<y?y:x;
}
inline ll Min(ll x,ll y){
    return x<y?x:y;
}
inline void Get(int x){
    s[x]=1;F[x]=0;
    for(int i=h[x];i;i=e[i].nx)
    if(!b[e[i].t]){
        Get(e[i].t);
        s[x]+=s[e[i].t];
        F[x]=Max(F[x],s[e[i].t]);
    }
    F[x]=Max(F[x],F[0]-s[x]);
    if(F[x]<F[Rt]&&s[x]>1)Rt=x;
}
inline double Slop(int x,int y){
    return (double)(Ans[y]-Ans[x])/(double)(d[y]-d[x]);
}
inline void Update(int x){
    if(!top)return;
    int l=1,r=top,Mid;
    while(l<=r){
        Mid=l+r>>1;
        if(Mid==top)break;
        if(Slop(ch[Mid],ch[Mid+1])>=p[x])l=Mid+1;else r=Mid-1;
    }
    Ans[x]=Min(Ans[x],Ans[ch[l]]+(d[x]-d[ch[l]])*p[x]+q[x]);
}
inline void Add(int x){
    a[++cnt]=Node(x,d[x]-l[x]);
    for(int i=h[x];i;i=e[i].nx)
    if(!b[e[i].t])Add(e[i].t);
}
inline bool Cmp(Node a,Node b){
    return a.w>b.w; 
}
inline void Insert(int x){
    while(top>1&&Slop(ch[top-1],x)<Slop(ch[top],x)+Eps)top--;
    ch[++top]=x;
}
inline void Solve(int x,int S){
    if(S==1)return;
    F[Rt=0]=S;Get(x);
    for(int i=h[Rt];i;i=e[i].nx)b[e[i].t]=1;
    int R=Rt;
    Solve(x,S-s[R]+1);cnt=top=0;
    for(int i=h[R];i;i=e[i].nx)Add(e[i].t);
    int i=R;
    sort(a+1,a+cnt+1,Cmp);
    for(int j=1;j<=cnt;j++){
        for(;i!=f[x]&&d[i]>=a[j].w;i=f[i])Insert(i);
        Update(a[j].f);
    }
    for(int i=h[R];i;i=e[i].nx)Solve(e[i].t,s[e[i].t]);
}
int main(){
    Read(n);Read(x);
    for(i=2;i<=n;i++){
        Read(f[i]);Read(z);Read(p[i]);Read(q[i]);Read(l[i]);
        Add(f[i],i,z);
    }
    Dfs1(1);
    memset(Ans,0x3f,sizeof(Ans));
    Ans[1]=0;Solve(1,n);
    for(i=2;i<=n;i++)Print(Ans[i]==Ans[0]?0:Ans[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值