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\n",dp[i]);
    return 0;
}

 

posted @ 2018-12-28 14:02  LiGuanlin  阅读(173)  评论(0编辑  收藏  举报