zoukankan      html  css  js  c++  java
  • [Noi2014]购票

    来自FallDream 的博客,未经允许,请勿转载,谢谢。


    今年夏天,NOI在SZ市迎来了她30周岁的生日。来自全国 n 个城市的OIer们都会从各地出发,到SZ市参加这次盛会。

           全国的城市构成了一棵以SZ市为根的有根树,每个城市与它的父亲用道路连接。为了方便起见,我们将全国的 n 个城市用 1 到 n 的整数编号。其中SZ市的编号为 1。对于除SZ市之外的任意一个城市 v,我们给出了它在这棵树上的父亲城市 fv  以及到父亲城市道路的长度 sv。
    从城市 v 前往SZ市的方法为:选择城市 v 的一个祖先 a,支付购票的费用,乘坐交通工具到达 a。再选择城市 a 的一个祖先 b,支付费用并到达 b。以此类推,直至到达SZ市。
    对于任意一个城市 v,我们会给出一个交通工具的距离限制 lv。对于城市 v 的祖先 a,只有当它们之间所有道路的总长度不超过 lv  时,从城市 v 才可以通过一次购票到达城市 a,否则不能通过一次购票到达。对于每个城市 v,我们还会给出两个非负整数 pv,qv  作为票价参数。若城市 v 到城市 a 所有道路的总长度为 d,那么从城市 v 到城市 a 购买的票价为 dpv+qv。
    每个城市的OIer都希望自己到达SZ市时,用于购票的总资金最少。你的任务就是,告诉每个城市的OIer他们所花的最少资金是多少。
    n<=2*10^5

    dp式子比较容易列出来 f[i]=min f[j]+q[i]+p[i]*(d[i]-d[j)

    考虑斜率优化,发现j比k优,当且仅当p<(f[j]-f[k])/(d[j]-d[k]) 所以考虑维护凸包

    可以直接线段树+无旋treap 大力维护凸包

    也可以cdq分治上树,每次像点分治那样做,然后用包含根的那个子树更新其它子树。

    更新时候按照深度(用于更新的点直接放深度,否则放深度减去limit)排序从下往上做 

    复杂度nlogn^2

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    #define MN 200000
    #define INF 2000000000
    using namespace std;
    inline ll read()
    {
        ll x = 0 , f = 1; char ch = getchar();
        while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
        while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
        return x * f;
    }
     
    ll q[MN+5],p[MN+5],l[MN+5],dep[MN+5],f[MN+5];
    int n,head[MN+5],cnt=0,top,size[MN+5],b[MN+5],fa[MN+5],mx[MN+5],Mx[MN+5];
    struct edge{int to,next;ll w;}e[MN*2+5];
    inline double Getslop(int x,int y){return !y?-INF:(double)(f[y]-f[x])/(dep[y]-dep[x]);}
    struct data{int x,kind;ll dep;}a[MN+5];
    struct CH
    {
        int s[MN+5],tp;
        void clear() {tp=0;}
        void ins(int x)
        {
            while(tp>1&&Getslop(s[tp],s[tp-1])<Getslop(x,s[tp])) --tp;
            s[++tp]=x;
        }   
        int query(ll p)
        {   
            int l=1,r=tp,mid,ans;s[tp+1]=0;
            while(l<=r)
            {
                mid=l+r>>1;
                if(Getslop(s[mid],s[mid+1])<(double)p) ans=mid,r=mid-1;
                else l=mid+1;
            }
            return s[ans];
        }
    }H;
    inline void ins(int f,int t,ll w){e[++cnt]=(edge){t,head[f],w};head[f]=cnt;}
     
    void Pre(int x)
    {
        size[x]=1;mx[x]=0;
        for(int i=head[x];i;i=e[i].next) 
            dep[e[i].to]=dep[x]+e[i].w,Pre(e[i].to),size[x]+=size[e[i].to],mx[x]=max(mx[x],size[e[i].to]);
    }
     
    void GetRt(int x,int tot,int &rt)
    {
        size[x]=1;mx[x]=0;
        for(int i=head[x];i;i=e[i].next)
            if(!b[e[i].to]) GetRt(e[i].to,tot,rt),size[x]+=size[e[i].to],mx[x]=max(mx[x],size[e[i].to]);
        Mx[x]=max(mx[x],tot-size[x]);
        if(size[x]>1&&Mx[x]<Mx[rt]) rt=x;
    }
     
    void Dfs(int x)
    {
        a[++top]=(data){x,0,dep[x]-l[x]};   
        for(int i=head[x];i;i=e[i].next)
            if(!b[e[i].to]) Dfs(e[i].to);
    }
    bool cmp(data x,data y){return x.dep==y.dep?x.kind>y.kind:x.dep>y.dep;}
    ll GetAns(int x,int y){return f[y]+p[x]*(dep[x]-dep[y])+q[x];}
    void Solve(int x,int Sz)
    {
        if(Sz<=1) return;int rt=0;
        GetRt(x,Sz,rt);  
        for(int i=head[rt];i;i=e[i].next) b[e[i].to]=1;
        Solve(x,Sz-size[rt]+1);
        a[top=1]=(data){rt,1,dep[rt]};
        for(int t=fa[rt];t!=fa[x];t=fa[t]) a[++top]=(data){t,1,dep[t]};
        for(int i=head[rt];i;i=e[i].next) Dfs(e[i].to);
        sort(a+1,a+top+1,cmp);H.clear();
        for(int i=1;i<=top;++i)
            if(a[i].kind) H.ins(a[i].x);
            else if(H.tp) f[a[i].x]=min(f[a[i].x],GetAns(a[i].x,H.query(p[a[i].x])));
        for(int i=head[rt];i;i=e[i].next) Solve(e[i].to,size[e[i].to]);
    } 
     
    int main()
    {
        n=read();read();
        memset(f,42,sizeof(f));
        for(int i=2;i<=n;++i)
        {
            fa[i]=read();ll w=read();
            p[i]=read(),q[i]=read();l[i]=read();
            ins(fa[i],i,w); 
        }
        Pre(1);f[1]=0;Mx[0]=INF;Solve(1,size[1]);
        for(int i=2;i<=n;++i) printf("%lld
    ",f[i]);
        return 0;
    }
     
  • 相关阅读:
    [生活] 日常英语学习笔记-NEVER HAVE I EVER游戏
    [PHP] 网盘搜索引擎-采集爬取百度网盘分享文件实现网盘搜索(二)
    [PHP] 网盘搜索引擎-采集爬取百度网盘分享文件实现网盘搜索
    [Linux] PHP程序员玩转Linux系列-telnet轻松使用邮箱
    [Linux] PHP程序员玩转Linux系列-升级PHP到PHP7
    [Linux] PHP程序员玩转Linux系列-使用supervisor实现守护进程
    [Linux] PHP程序员玩转Linux系列-Nginx中的HTTPS
    [Linux] PHP程序员玩转Linux系列-nginx初学者引导
    [Linux] PHP程序员玩转Linux系列-Linux和Windows安装nginx
    [Linux] PHP程序员玩转Linux系列-自动备份与SVN
  • 原文地址:https://www.cnblogs.com/FallDream/p/Noi2014d2t3.html
Copyright © 2011-2022 走看看