zoukankan      html  css  js  c++  java
  • BZOJ_3672_ [Noi2014]购票_CDQ分治+斜率优化

    BZOJ_3672_ [Noi2014]购票_CDQ分治+斜率优化

    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

    $O(n^2)$DP:f[x]=f[p]+(dep[x]-dep[p])*P[x]+Q[x]  (dep[x]-dep[p]<=L[x])
    如果是在序列上可以用斜率优化+二分或CDQ分治的方法解决。
    但是这是一棵树。
    考虑继续使用CDQ分治,假设当前solve的是x这棵子树,每次找到一个分治点p,从x中把p的子树抠掉。
    先处理剩下的,然后计算x到p链上的点对p的子树的影响,然后递归p的子树。
    然后这个分治点肯定要使得递归的两部分大小差不多大,需要每次求一下中心。
    考虑x到p的链上的点对p的子树的影响怎么计算。
    链上的点对dep升序,子树内的点按dep[x]-L[x]降序,这样决策点那边指针单调。
    每次二分一下斜率就做完了。
    细节还是有一些的,不过重要的还是怎么想到这种分治做法的。
     
    代码:
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cstdlib>
    using namespace std;
    typedef long long ll;
    #define N 200050
    typedef double f2;
    int head[N],to[N],nxt[N],fa[N],n,cnt,mx[N],tot,root,siz[N],S[N],top,vis[N];
    ll val[N],dep[N],P[N],Q[N],L[N],f[N];
    int a[N],b[N],la,lb;
    inline void add(int u,int v,ll w) {
        to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt; val[cnt]=w;
    }
    bool cmp1(const int &x,const int &y) {
        return dep[x]-L[x]>dep[y]-L[y];
    }
    f2 K(int i,int j) {
        return (f2(f[i]-f[j]))/(dep[i]-dep[j]);
    }
    void dfs(int x) {
        int i; siz[x]=1;
        for(i=head[x];i;i=nxt[i]) {
            dep[to[i]]=dep[x]+val[i];
            dfs(to[i]);
            siz[x]+=siz[to[i]];
        }
    }
    void get_root(int x) {
        int i;
        siz[x]=1; mx[x]=0;
        for(i=head[x];i;i=nxt[i]) if(!vis[to[i]]) {
            get_root(to[i]);
            siz[x]+=siz[to[i]];
            mx[x]=max(mx[x],siz[to[i]]);
        }
        mx[x]=max(mx[x],tot-siz[x]);
        if(mx[root]>mx[x]) root=x;
    }
    void diu1(int x,int y) {
        int p;
        for(p=x;p!=fa[y];p=fa[p]) a[++la]=p;
    }
    void diu2(int x) {
        int i; b[++lb]=x;
        for(i=head[x];i;i=nxt[i]) if(!vis[to[i]]) {
            diu2(to[i]);
        }
    }
    void solve(int x) {
        tot=siz[x]; root=0; get_root(x); int rt=root; vis[rt]=1;
        //printf("%d %d
    ",x,rt);
        if(x!=rt) siz[x]-=siz[rt],solve(x);
        la=lb=0;
        int i,j;
        diu1(rt,x);
        for(i=head[rt];i;i=nxt[i]) if(!vis[to[i]]) {
            diu2(to[i]);
        }//printf("%d %d
    ",la,lb);
        for(i=2;i<=la;i++) if(dep[rt]-dep[a[i]]<=L[rt]) f[rt]=min(f[rt],f[a[i]]+(dep[rt]-dep[a[i]])*P[rt]+Q[rt]);
         
        sort(b+1,b+lb+1,cmp1);
         
        top=0,S[0]=0;
        for(j=1,i=1;i<=lb;i++) {
            int u=b[i];
            while(j<=la&&dep[u]-dep[a[j]]<=L[u]) {
                while(top>1&&K(S[top],a[j])>K(S[top-1],S[top])) top--;
                S[++top]=a[j++];
            }
            if(!top) continue;
            if(top==1) f[u]=min(f[u],f[S[1]]+(dep[u]-dep[S[1]])*P[u]+Q[u]);
            else {
                int l=1,r=top;
                while(l<r) {
                    int mid=(l+r)>>1;
                    if(P[u]>K(S[mid],S[mid+1])) r=mid;
                    else l=mid+1;
                }
                f[u]=min(f[u],f[S[l]]+(dep[u]-dep[S[l]])*P[u]+Q[u]);
            }
        }
     
        for(i=head[rt];i;i=nxt[i]) if(!vis[to[i]]) {
            solve(to[i]);
        }
    }
    int main() {
        mx[0]=1<<30;
        scanf("%d%*d",&n);
        int i;
        ll x;
        for(i=2;i<=n;i++) {
            scanf("%d%lld%lld%lld%lld",&fa[i],&x,&P[i],&Q[i],&L[i]); add(fa[i],i,x);
        }
        memset(f,0x3f,sizeof(f));
        f[1]=0;
        dfs(1);
        solve(1);
        for(i=2;i<=n;i++) printf("%lld
    ",f[i]);
    }
    
  • 相关阅读:
    Linux中history执行历史命令方法
    Linux中返回上一次目录
    Linux的vi编辑模式下常用快捷键
    [Android] TextView上同时显示图标和文字
    [Android] macOS的Android Studio快捷键
    [Android] 转-RxJava+MVP+Retrofit+Dagger2+Okhttp大杂烩
    [iOS] 测试设备解决自签名证书问题
    [macOS] keychain的跳坑之旅!git拉取的权限问题
    [PHP] swoole在daemonize模式下,chdir失效问题
    [macOS] macOS下,VirtualBox安装CentOS7.4, 搭建nginx, mysql, PHP5.6&PHP7.1
  • 原文地址:https://www.cnblogs.com/suika/p/9427051.html
Copyright © 2011-2022 走看看