购票

购票

题目放个传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3672

题解

很容易推出暴力dp:f[i]=f[j]+p[i]*(dep[i]-dep[j])+q[i]。
我们可以设k=dep[j],b=f[j],x=p[i],如果是链斜率优化即可。不是链的话,每次把每个节点到根的路径取出来处理即可。
正解:点分治+cdq分治。(本蒟蒻太懒(弱)了不想(会)写。)
本蒟蒻写的暴力:树链剖分,对于每个线段树结点建立一个凸包,每次查询操作在凸包上二分斜率即可。复杂度O(n*log(n)^3),看起来很吓人对吧,然而仔细一看不难发现常数极小,可以轻松水过。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<vector>
#include<algorithm>
#define ll long long
#define inf 99999999999999999ll
#define N 200010
using namespace std;
int n,fa[N],p[N];
int size[N],top[N],next[N],flag[N];
int st[N],end[N],pos[N],num[N],pl[N],tot,rt[N];
int la[N],ff[N],Q[N],s[N*18],tmp,cnt;
ll f[N],len[N],q[N],dis[N];
struct node{int a,b;ll c;}map[N];
struct point{
  int lc,rc,fa,l,sum;
  double cal(int x,int y)
  {
    return double(f[x]-f[y])/(dis[x]-dis[y]);
  }
  void insert(int x)
  {
    if(sum<2){s[l+sum]=x,sum++;return;}
    while(sum-1&&cal(s[l+sum-2],s[l+sum-1])>=cal(s[l+sum-2],x))sum--;
    s[l+sum]=x;sum++;
  }
  ll qry(int x)
  {
    int lx=l,rx=l+sum-1;
    while(rx-lx>1)
    {
      int mid=lx+rx>>1;
      if(cal(s[mid],s[mid+1])>p[x])rx=mid;
      else lx=mid+1;
    }
    return min(f[s[lx]]-p[x]*dis[s[lx]],f[s[rx]]-p[x]*dis[s[rx]]);
  }
}t[N*4];

class seg_tree
{
  vector<pair<ll,int> >tp;
  ll qry(int x,int l,int r,int ql,int qr,int k)
  {
    if(ql<=l&&r<=qr){return t[x].qry(k);}
    int lc=t[x].lc,rc=t[x].rc,mid=l+r>>1;
    ll res=inf;
    if(ql<=mid)res=min(res,qry(lc,l,mid,ql,qr,k));
    if(qr>mid)res=min(res,qry(rc,mid+1,r,ql,qr,k));
    return res;
  }
  public:
  void build(int x,int l,int r)
  {
    t[x].l=tmp+1;t[x].sum=0;tmp+=r-l+1;
    if(l==r){pl[num[l]]=x;return;}
    int mid=l+r>>1;
    build(t[x].lc=++cnt,l,mid);
    build(t[x].rc=++cnt,mid+1,r);
    t[t[x].lc].fa=x;t[t[x].rc].fa=x;
  }
  ll query(int x,int ql,int qr,int k)
  {
    if(tp.empty()||dis[k]-len[k]>dis[num[qr]])return inf;
    if(dis[k]-len[k]<=dis[num[ql]])return qry(rt[x],st[x],end[x],ql,qr,k);
    int pp=lower_bound(tp.begin()+ql-1,tp.begin()+qr-1,make_pair(dis[k]-len[k],0))->second;
    return qry(rt[x],st[x],end[x],max(pp,ql),qr,k);
  }
  void insert(int x)
  {
    for(int p=pl[x];p;p=t[p].fa)t[p].insert(x);
    tp.push_back(make_pair(dis[x],pos[x]));
  }
}T[N];

void bfs()
{
  int l=1,r=2;Q[1]=1;
  while(l<r)
  {
    int x=Q[l];size[x]=1;l++;
    for(int a=la[x];a;a=ff[a])
      Q[r]=map[a].b,dis[Q[r]]=dis[x]+map[a].c,r++;
  }
  for(int i=r-1;i;i--)
  {
    int x=Q[i];size[fa[x]]+=size[x];
    if(size[next[fa[x]]]<size[x])next[fa[x]]=x;
  }
  for(int i=1;i<r;i++)
  {
    int x=Q[i];
    if(flag[x])continue;st[x]=tot+1;
    for(int j=x;j;j=next[j])
      pos[j]=++tot,num[tot]=j,top[j]=x,flag[j]=1;
    end[x]=tot;
    T[x].build(rt[x]=++cnt,st[x],end[x]);
  }
}

int main()
{
  int a;ll c;
  scanf("%d%d",&n,&a);
  for(int i=2;i<=n;i++)
  {
    scanf("%d%lld%d%lld%lld",&a,&c,&p[i],&q[i],&len[i]);
    map[i]=(node){a,i,c};ff[i]=la[a];la[a]=i;fa[i]=a;
  }
  bfs();T[1].insert(1);
  for(int i=2;i<=n;i++)
  {
    int v=Q[i],x,y;
    f[v]=T[top[v]].query(top[v],st[top[v]],pos[v]-1,v);
    x=fa[top[v]];y=top[x];
    for(;x;x=fa[y],y=top[x])
      f[v]=min(f[v],T[y].query(y,st[y],pos[x],v));
    f[v]+=(ll)p[v]*dis[v]+q[v];T[top[v]].insert(v);
  }
  for(int i=2;i<=n;i++)printf("%lld\n",f[i]);
  return 0;
}

你可能感兴趣的:(购票)