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

    设f[x]为从x跑到1的最小花费,基本转移如下

    [left{ egin{aligned} f[1]&=0\ f[x]&=min_{dep[x]-dep[y]le l[x]} f[y]+p[x](dep[x]-dep[y])+q[x]\ &=p[x]dep[x]+q[x]+min_{dep[x]-dep[y]le l[x]} f[y]-p[x]dep[y] end{aligned} ight. ]

    有斜率优化的影子

    [f[x]=p[x]dep[x]+q[x]+(f[y]-p[x]dep[y])\ f[y]=underline{p[x]}dep[y]+underline{f[x]-(p[x]dep[x]+q[x])} ]

    于是在dp的过程中需要维护根到x父亲的一条链,二分求出被x接受的链的后缀,需要得到后缀上点(dep,f)的凸包,然后二分斜率求答案 。压入点、弹出点、计算后缀的凸包……显然这时间爆炸。

    注意到这个后缀是可以分成若干段分别计算的,考虑对栈优雅地分块?时间复杂度(O(nsqrt nlog n))不太友好。

    可以使用带撤销的二进制分组来肝,即用线段树来做一些完整区间(为方便处理,提前把树处理成满二叉树)的凸包,除了每层的最后一的满区间。

    (其实这根树链剖分的做法已经很相似了啊)

    #include <bits/stdc++.h>
    #define ll long long 
    using namespace std;
    
    const int N=2e5+10;
    const int M=N*8;
    
    int n;
    int head[N],to[N],lst[N];
    int fa[N][20],dep[N],mxd;
    ll wit[N],dis[N],P[N],Q[N],L[N],f[N];
    
    int len=1,id[N*2];
    namespace SGT {
        int bel[M],num[M],siz[M],cnt[25],tmp[N];
        vector<int> tre[M];
        bool hav[M];
    #define ls (x<<1)
    #define rs (x<<1|1)
        void build(int x,int l,int r) {
            num[x]=++tmp[bel[x]=id[r-l+1]];
            if(l==r) {tre[x].resize(1); return;}
            int mid=(l+r)>>1;
            build(ls,l,mid);
            build(rs,mid+1,r);
        }
        void upd(int x,int l,int r,int p,int k) {
            if(++siz[x]==r-l+1) ++cnt[bel[x]];
            if(l==r) {
                tre[x][0]=k;
                hav[x]=true;
                return;
            }
            int mid=(l+r)>>1;
            if(p<=mid) upd(ls,l,mid,p,k);
            else upd(rs,mid+1,r,p,k);
        }
        void del(int x,int l,int r,int p) {
            hav[x]=false;
            if(--siz[x]==r-l) --cnt[bel[x]];
            if(l==r) return;
            int mid=(l+r)>>1;
            if(p<=mid) del(ls,l,mid,p);
            else del(rs,mid+1,r,p);
        }
    #define X(i) dis[i]
    #define Y(i) f[i]
        inline ll calc(int y,int x) {return f[y]+P[x]*(dis[x]-dis[y])+Q[x];}
        inline bool cross(ll x1,ll y1,ll x2,ll y2) {return 1.0*x1*y2>=1.0*x2*y1;}
        inline void merge(vector<int> &a,vector<int> &l,vector<int> &r) {
            int top=0;
            for(auto i:l) tmp[++top]=i;
            for(auto i:r) {
                while(top>1&&cross(X(i)-X(tmp[top-1]),Y(i)-Y(tmp[top-1]), // 凸壳部分
                    X(tmp[top])-X(tmp[top-1]),Y(tmp[top])-Y(tmp[top-1])))
                    top--;
                tmp[++top]=i;
            }
            a.resize(top);
            for(int i=1; i<=top; ++i) a[i-1]=tmp[i];
        }
        void work(int x,int l,int r) {
            if(hav[x]) return;
            hav[x]=true;
            int mid=(l+r)>>1;
            work(ls,l,mid);
            work(rs,mid+1,r);
            merge(tre[x],tre[ls],tre[rs]);
        }
        inline ll calc(vector<int>& a,int x) {
            int l=0,r=a.size()-2,mid;
            ll ans=calc(a.back(),x),K=P[x];
            while(l<=r) {
                mid=(l+r)>>1;
                ans=min(ans,calc(a[mid],x));
                if(cross(1,K,X(a[mid+1])-X(a[mid]),Y(a[mid+1])-Y(a[mid]))) r=mid-1;
                else l=mid+1;
            }
            return ans;
        }
        ll query(int x,int l,int r,int L,int R,int X) {
            if(L<=l&&r<=R) {
                if(!hav[x]&&num[x]<cnt[bel[x]]) work(x,l,r);
                if(hav[x]) return calc(tre[x],X);
            }
            int mid=(l+r)>>1; ll ans=1e18;
            if(L<=mid) ans=query(ls,l,mid,L,R,X);
            if(mid<R) ans=min(ans,query(rs,mid+1,r,L,R,X));
            return ans;
        }
    }
    
    inline void add_edge(int x,int y,int w) {
        static int cnt=0;
        to[++cnt]=y;
        wit[cnt]=w;
        lst[cnt]=head[x];
        head[x]=cnt;
    }
    inline ll calc(int x) {
        int z=fa[x][0],y=x;
        for(int i=19; ~i; --i) 
            if(fa[y][i]&&dis[x]-dis[fa[y][i]]<=L[x]) y=fa[y][i];
        return SGT::query(1,1,len,dep[y],dep[z],x);
    }
    void pre(int x) {
        mxd=max(mxd,dep[x]=dep[fa[x][0]]+1);
        for(int i=1; (1<<i)<=dep[x]; ++i) fa[x][i]=fa[fa[x][i-1]][i-1];
        for(int i=head[x]; i; i=lst[i]) {
            dis[to[i]]=dis[x]+wit[i]; 
            pre(to[i]);
        }
    }
    void dfs(int x) {
        if(x!=1) f[x]=calc(x);
        SGT::upd(1,1,len,dep[x],x);
        for(int i=head[x]; i; i=lst[i]) dfs(to[i]);
        SGT::del(1,1,len,dep[x]);
    }
    
    int main() {
        ll val;
        scanf("%d%lld",&n,&val);
        for(int i=2; i<=n; ++i) {
            scanf("%d%lld%lld%lld%lld",&fa[i][0],&val,P+i,Q+i,L+i);
            add_edge(fa[i][0],i,val);
        }
        pre(1);
        for(int i=1; len<mxd; len<<=1,++i) id[len]=i;
        SGT::build(1,1,len);
        dfs(1);
        for(int i=2; i<=n; ++i) printf("%lld
    ",f[i]);
        return 0;
    }
    

    代码大规模借鉴@i207M

  • 相关阅读:
    Solidity safesub防止溢出
    Solidity字符串拼接实现oraclize动态查询
    Solidity mapping循环
    Solidity 合约调用合约
    Solidity string to uint
    Solidity智能合约升级解决方案
    Solidity部署问题
    linux 安装xwiki
    linux 安装 java
    linux 安装tomcat
  • 原文地址:https://www.cnblogs.com/nosta/p/10919094.html
Copyright © 2011-2022 走看看