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

    题目描述

    题解:

    这位仁兄您点进来的题解是cdq+点分+斜率优化的。

    吐草:细节是真多……

    先推一波式子:

    $dp[i]=min(dp[j]+(dis[i]-dis[j])*p[i]+q[i])=dis[i]*p[i]+q[i]+min(dp[j]-dis[j]*p[i])$

    $min()$里面那个明显是斜率优化。

    这个转移要求更新i时1~i路径上的所有点均已经是最优解。

    然后你就想到了$cdq$分治。

    $cdq$分治一段区间操作:分治左区间,处理左区间->右区间,分治右区间;

    毒瘤题购票树上操作:处理深度浅的,处理浅的->深的,处理深度深的。

    这好像是一样的。

    所以我们可以把$cdq$中的$mid$换成点分中的重心。

    然后重心上面的更新重心和重心下面的,再让重心更新重心下面的。

    然后就过了。

    ~~~

    有几个大坑:

    1.叉积爆$long long$,要用$double$除法。

    2.由于让$b$值最小我们应该维护下凸包,但是$dis$是倒序加入。不妨将$x,y$都取反,然后维护正向加入的上凸包。

    代码:

    #include<vector>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define N 200050
    #define ll long long 
    #define db long double
    #define nd (ve.size()-1)
    const int inf = 0x3f3f3f3f;
    const db eps = 1e-8;
    inline ll rd()
    {
        ll f=1,c=0;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){c=c*10+ch-'0';ch=getchar();}
        return f*c;
    }
    int n,t,hed[N],cnt;
    struct pnt
    {
        int fa;
        ll p,q,l;
    }c[N];
    struct EG
    {
        int to,nxt;
        ll v;
    }e[2*N];
    void ae(int f,int t,ll v)
    {
        e[++cnt].to = t;
        e[cnt].nxt = hed[f];
        e[cnt].v = v;
        hed[f] = cnt;
    }
    ll dis[N];
    void dfs(int u,int fa)
    {
        for(int j=hed[u];j;j=e[j].nxt)
        {
            int to = e[j].to;
            if(to==fa)continue;
            dis[to] = dis[u] + e[j].v;
            dfs(to,u);
        }
    }
    int rt,sum,mrk[N],w[N],siz[N];
    void get_rt(int u,int fa)
    {
        w[u] = 0,siz[u] = 1;
        for(int j=hed[u];j;j=e[j].nxt)
        {
            int to = e[j].to;
            if(to==fa||mrk[to])continue;
            get_rt(to,u);
            siz[u]+=siz[to];
            if(siz[to]>w[u])w[u] = siz[to];
        }
        w[u] = max(w[u],sum-siz[u]);
        if(w[u]<w[rt])rt=u;
    }
    ll dp[N];
    int pu[N],pd[N];
    bool cmp(int x,int y)
    {
        return dis[x]-c[x].l>dis[y]-c[y].l;
    }
    int tot1,tot2;
    void push_up(int p1,int p2)
    {
        tot1 = 0;
        while(p2!=p1)
        {
            p2 = c[p2].fa;
            pu[++tot1] = p2;
        }
    }
    void fill(int u)
    {
        pd[++tot2] = u;
        for(int j=hed[u];j;j=e[j].nxt)
        {
            int to = e[j].to;
            if(to==c[u].fa||mrk[to])continue;
            fill(to);
        }
    }
    void push_down(int p2)
    {
        tot2 = 0;fill(p2);
        sort(pd+1,pd+1+tot2,cmp);
    }
    double slop(int a,int b)
    {
        return (double)(dp[a]-dp[b])/(dis[a]-dis[b]);
    }
    int fd(db k,vector<int>&ve)
    {
        int l = 0,r = nd-1,ans = nd;
        while(l<=r)
        {
            int mid = (l+r)>>1;
            if(slop(pu[ve[mid]],pu[ve[mid+1]])<k)
            {
                ans = mid;
                r = mid-1;
            }else l = mid+1;
        }
        return ans;
    }
    void cdq(int p1,int p2)
    {
        mrk[p2] = 1;
        if(p1!=p2)
        {
            rt = 0,sum = siz[p1] - siz[p2];
            get_rt(p1,0);
            cdq(p1,rt);
        }
        push_up(p1,p2);
        push_down(p2);
        int l = 1,r = 1;
        vector<int>ve;
        for(;r<=tot2;r++)
        {
            while(l<=tot1&&dis[pu[l]]>=dis[pd[r]]-c[pd[r]].l)
            {
                while(ve.size()>=2&&slop(pu[l],pu[ve[nd]])>=slop(pu[ve[nd]],pu[ve[nd-1]]))
                    ve.pop_back();
                ve.push_back(l);
                l++;
            }
            if(!ve.size())continue;
            int u = pu[ve[fd(c[pd[r]].p,ve)]],v = pd[r];
            dp[v] = min(dp[v],dp[u]-dis[u]*c[v].p+c[v].q);
        }
        for(r=tot2;dis[pd[r]]-c[pd[r]].l<=dis[p2]&&r>=1;r--)
        {
            int v = pd[r],u = p2;
            dp[v] = min(dp[v],dp[u]-dis[u]*c[v].p+c[v].q);
        }
        for(int j=hed[p2];j;j=e[j].nxt)
        {
            int to = e[j].to;
            if(to==c[p2].fa||mrk[to])continue;
            rt=0,sum=siz[to];
            get_rt(to,0);
            cdq(to,rt);
        }
    }
    int main()
    {
    //    freopen("1.in","r",stdin);
        n = rd(),t = rd();ll s;
        for(int f,i=2;i<=n;i++)
        {
            f = rd(),s = rd(),c[i].p = rd(),c[i].q = rd(),c[i].l = rd();
            ae(f,i,s),ae(i,f,s);
            c[i].fa = f;
        }
        dfs(1,0);
        for(int i=2;i<=n;i++)c[i].q+=dis[i]*c[i].p;
        memset(dp,0x3f,sizeof(dp));
        w[0] = inf;dp[1]=0;
        rt=0,sum=n;
        get_rt(1,0);
        cdq(1,rt);
        for(int i=2;i<=n;i++)
            printf("%lld
    ",dp[i]);
        return 0;
    }
  • 相关阅读:
    Openwave V7 不支持中文的解决方法
    VBS的疑惑,它们不考虑效率吗?
    删除顽固 NTServic和webacc.exe病毒。
    我的电脑怎么多了一些乱七八糟的东西。
    阿怒再发,突然的发现,为了编码输入速度!
    庆祝开博,也算给自己加油!
    超级简单的工厂模式温度转换
    阿怒乱弹之VS05重构的提取方法操作不方便啊!
    随笔嘛!就是随便下笔~呵呵!
    Oracle数据库一样平常维护手册2
  • 原文地址:https://www.cnblogs.com/LiGuanlin1124/p/10190196.html
Copyright © 2011-2022 走看看