• [BZOJ]3672: [Noi2014]购票


    Time Limit: 30 Sec  Memory Limit: 512 MB

    Description

      今年夏天,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他们所花的最少资金是多少。

    Input

      第 1 行包含2个非负整数 n,t,分别表示城市的个数和数据类型(其意义将在后面提到)。输入文件的第 2 到 n 行,每行描述一个除SZ之外的城市。其中第 v 行包含 5 个非负整数 f_v,s_v,p_v,q_v,l_v,分别表示城市 v 的父亲城市,它到父亲城市道路的长度,票价的两个参数和距离限制。请注意:输入不包含编号为 1 的SZ市,第 2 行到第 n 行分别描述的是城市 2 到城市 n。

    Output

      输出包含 n-1 行,每行包含一个整数。其中第 v 行表示从城市 v+1 出发,到达SZ市最少的购票费用。同样请注意:输出不包含编号为 1 的SZ市。

    Sample Input

      7 3
      1 2 20 0 3
      1 5 10 100 5
      2 4 10 10 10
      2 9 1 100 10
      3 5 20 100 10
      4 4 20 0 10

    Sample Output

      40
      150
      70
      149
      300
      150

    HINT

      对于所有测试数据,保证 0≤pv≤106,0≤qv≤1012,1≤fv<v;保证 0<sv≤lv≤2×1011,且任意城市到SZ市的总路程长度不超过 2×1011

      输入的 t 表示数据类型,0≤t<4,其中:

      当 t=0 或 2 时,对输入的所有城市 v,都有 fv=v-1,即所有城市构成一个以SZ市为终点的链;

      当 t=0 或 1 时,对输入的所有城市 v,都有 lv=2×1011,即没有移动的距离限制,每个城市都能到达它的所有祖先;

      当 t=3 时,数据没有特殊性质。

      n=2×10^5

    Solution

      此题有很妙的树分治解法,但我觉得还不如我的树套树解法容易实现,于是我就大力树套树了。

      容易得出DP方程f[i]=max(f[j]+p[i]*(d[i]-d[j])+q[i]),其中j是i的祖先,d[i]-d[j]<=l[i],d[i]表示i到根的距离,考虑斜率优化,得出i从j转移优于从k转移(d[j]>d[k])的条件是(f[j]-f[k])/(d[j]-d[k])<p[i],对每个点,我们可以把能转移到它的点按深度顺序加入凸壳中,维护斜率单调上升,然后拿p[i]进去二分就能找到最优转移点,考虑用数据结构维护这个过程,从根开始向下dfs,用无旋treap维护把当前dfs到的点到根路径上的所有点依次加入得到的凸壳,插入的时候直接把多余的点split出来,撤销的时候把之前split出来的直接merge回去就能保证复杂度,由于转移有深度限制,我们用线段树套treap,每次二分一下最远的转移点然后去线段树上查就好了,复杂度O(nlogn^2)。

    Code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    using namespace std;
    #define ll long long
    inline ll read()
    {
        ll x;char c;
        while((c=getchar())<'0'||c>'9');
        for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=x*10+c-'0';
        return x;
    }
    #define MN 200000
    #define N 262144
    #define ND 4000000
    struct edge{int nx,t;}e[MN+5];
    int h[MN+5],en,fa[MN+5],p[MN+5],z[MN+5],zn,Tn;
    ll d[MN+5],q[MN+5],lm[MN+5],f[MN+5];
    inline void insEdge(int x,int y){e[++en]=(edge){h[x],y};h[x]=en;}
    struct treap
    {
        treap*l,*r;int p;ll f,d;double x;
        inline int ran()
        {
            static int x=23333;
            return x^=x<<13,x^=x>>17,x^=x<<5;
        }
        treap(ll f=0,ll d=0,double x=0):f(f),d(d),x(x){l=r=0;p=ran();}
    }*t[N*2+5],T[ND+5];
    vector<treap*> v[MN+5];
    treap*merge(treap*a,treap*b)
    {
        if(!a)return b;if(!b)return a;
        if(a->p>b->p)return a->r=merge(a->r,b),a;
        return b->l=merge(a,b->l),b;
    }
    void query(treap*k,int x)
    {
        if(!k)return;
        f[x]=min(f[x],k->f+p[x]*(d[x]-k->d)+q[x]);
        query(k->x<p[x]?k->r:k->l,x);
    }
    void split(treap*k,int x,treap*&a,treap*&b)
    {
        if(!k){a=b=0;return;}
        if((f[x]-k->f)/(d[x]-k->d)<k->x)split(k->l,x,a,k->l),b=k;
        else split(k->r,x,k->r,b),a=k;
    }
    void query(int l,int r,int x)
    {
        for(l+=N-1,r+=N+1;l^r^1;l>>=1,r>>=1)
        {
            if(~l&1)query(t[l+1],x);
            if( r&1)query(t[r-1],x);
        }
    }
    void ins(int k,int x)
    {
        for(k+=N;k;k>>=1)
        {
            treap*p;
            split(t[k],x,t[k],p);
            v[x].push_back(p);
            if(t[k])
            {
                for(p=t[k];p->r;)p=p->r;
                double s=(f[x]-p->f)/(d[x]-p->d);
                T[++Tn]=treap(f[x],d[x],s);
            }
            else T[++Tn]=treap(f[x],d[x],-1e18);
            t[k]=merge(t[k],T+Tn);
        }
    }
    void del(int k,int x)
    {
        int i=0;
        for(k+=N;k;k>>=1)
        {
            treap**p;
            for(p=t+k;(*p)->r;p=&((*p)->r));
            *p=(*p)->l;
            t[k]=merge(t[k],v[x][i++]);
        }
    }
    void dfs(int x)
    {
        if(x==1)f[x]=0;
        else
        {
            int l=1,r=zn,mid,s;
            while(l<=r)
                if(d[x]-d[z[mid=l+r>>1]]<=lm[x])s=mid,r=mid-1;
                else l=mid+1;
            query(s,zn,x);
        }
        z[++zn]=x;ins(zn,x);
        for(int i=h[x];i;i=e[i].nx)dfs(e[i].t);
        del(zn,x);--zn;
    }
    int main()
    {
        int n,i;
        n=read();read();
        for(i=2;i<=n;++i)
        {
            insEdge(fa[i]=read(),i);d[i]=d[fa[i]]+read();
            p[i]=read();q[i]=read();lm[i]=read();
        }
        memset(f,127,sizeof(f));
        dfs(1);
        for(i=2;i<=n;++i)printf("%lld
    ",f[i]);
    }

     

     

  • 相关阅读:
    linux ftp启用和停用及vsftpd安装 ssh 启用和停用命令及ssh的安装
    linux 网络命令
    printf的使用
    Shell echo命令
    shell脚本之文件操作
    shell脚本之字符串运算的使用
    linux的计划任务操作
    系统操作有关的命令
    shell脚本之 operater.sh 算术运算符;比较运算符;布尔运算符;逻辑与或非运算符;字符串运算符的使用
    shell脚本的参数传递使用
  • 原文地址:https://www.cnblogs.com/ditoly/p/BZOJ3672.html
Copyright © 2020-2023  润新知