BZOJ3672 [Noi2014]购票

题解:

首先dp方程很简单 dp[i]=min(dp[j]+p[i]*dist(i,j)+q[i])

显然是可以斜率优化的

(dp[j]-dp[k])/(dis[j]-dis[k])<p[i]

那么我们的目的就是要维护这个凸包

由于p[i]不具有单调性,所以最优解是要三分的

然后我们先考虑这个问题在链上

现在的问题在于,还有li这个限制

于是我们可以选择建立线段树,对线段树每个区间维护其内的凸包

这样我们可以在nlog^2 n的时间内完成链上

考虑回到树上

我们可以直接树剖 这样是nlog^3 n的

由于树剖常数很小,所以也可以过啊

 线段树和树剖写的都很顺利。。然后就wa了

对拍了发现又犯了个智障错误。。 先改了ans再查。。

不知道为什么还能过那么多

为了比较简单 我都用了ll。。懒得改int

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define IL inline
#define rint register ll
#define rep(i,h,t) for (rint i=h;i<=t;i++)
#define dep(i,t,h) for (rint i=t;i>=h;i--)
IL ll max(ll x,ll y)
{
  if (x>y) return(x); else return(y);
}
IL ll min(ll x,ll y)
{
  if (x<y) return(x); else return(y); 
}
IL void swap(ll &x,ll &y)
{
  ll tmp=x; x=y; y=tmp;
}
char ss[1<<27],*A=ss,*B=ss;
IL char gc()
{
  return A==B&&(B=(A=ss)+fread(ss,1,1<<27,stdin),A==B)?EOF:*A++;
}
template<class T> void read(T &x)
{
  rint f=1,c; while (c=gc(),c<48||c>57) if (c=='-') f=-1; x=c^48;
  while (c=gc(),47<c&&c<58) x=(x<<3)+(x<<1)+(c^48); x*=f; 
}
#define mid ((h+t)/2)
const ll N=5.3e5;
const ll INF=1e18;
ll now[N],p[N],q[N],head[N],size[N],son[N],fa[N],top[N],id[N],real2[N],dep[N],dis[N],dp[N],l[N],lx,num;
ll bz1[20][N],bz2[20][N],n,m;
struct re{
  ll a,b,c,d;
}a[N];
vector<ll> ve[N*4];
vector<re> ve1[N];
IL void arr(ll x,ll y,ll z)
{
  a[++lx].a=head[x];
  a[lx].b=y;
  a[lx].c=z;
  head[x]=lx;
}
void dfs1(ll x,ll y)
{
  ll u=head[x];
  fa[x]=y; son[x]=-1; size[x]=1; dep[x]=dep[y]+1;
  while(u)
  {
    ll v=a[u].b;
    dis[v]=dis[x]+a[u].c;
    dfs1(v,x);
    bz1[0][v]=x;
    bz2[0][v]=a[u].c;
    size[x]+=size[v];
    if (son[x]==-1||size[v]>size[son[x]]) son[x]=v;
    u=a[u].a;
  }
}
void dfs2(ll x,ll y)
{
  top[x]=y; id[x]=++num; real2[num]=x;
  if (son[x]==-1) return;
  dfs2(son[x],y);
  ll u=head[x];
  while (u)
  {
    ll v=a[u].b;
    if (v!=son[x]) dfs2(v,v);
    u=a[u].a;
  }
}
IL double check1(ll x,ll y)
{
  return(1.00000000*(dp[x]-dp[y])/(dis[x]-dis[y]));
}
ll query(ll x,ll h,ll t,ll h1,ll t1,ll k)
{
  if (h1<=h&&t<=t1)
  {
    if (!now[x]) return(INF);
    ll l=0,r=now[x]-1;
    while (l<r)
    {
      ll midd=(l+r)/2;
      if (check1(ve[x][midd+1],ve[x][midd])<=p[k]) l=midd+1;
      else r=midd;
    }
    ll xx=ve[x][l];
    return(dp[xx]+p[k]*(dis[k]-dis[xx])+q[k]);
  }
  ll ans=INF;
  if (h1<=mid) ans=query(x*2,h,mid,h1,t1,k);
  if (mid<t1) ans=min(ans,query(x*2+1,mid+1,t,h1,t1,k));
  return(ans);
}
IL bool check2(ll x,ll y,ll z)
{
  if (1.00000000*(dp[z]-dp[x])/(dis[z]-dis[x])>=1.0000000*(dp[x]-dp[y])/(dis[x]-dis[y]))
    return(1); else return(0);
}
void change(ll x,ll h,ll t,ll pos,ll k)
{
  ll l=0,r=now[x]-1;
  while (l<r)
  {
    ll midd=(l+r)/2;
    if (check2(ve[x][midd+1],ve[x][midd],k)) l=midd+1;
    else r=midd;
  }
  //ve1[k].push_back((re){x,now[k],l+1,l+1<=now[k]?ve[k][l+1]:-1});
  if (!now[x]) now[x]=1; else now[x]=l+2;
  //if (now[x]>=ve[x].size()) ve[x].push_back(k); else ve[x][l+1]=k;
  if (now[x]>ve[x].size()) ve[x].push_back(k); else ve[x][now[x]-1]=k;
  if (h==t) return;
  if (pos<=mid) change(x*2,h,mid,pos,k);
  else change(x*2+1,mid+1,t,pos,k);
}
void dfs3(ll x)
{
  ll f1=top[x],xy=x;
  ll ans=INF;
  while (x&&dep[l[xy]]<=dep[x])
  {
    if (dep[l[xy]]<=dep[f1]) ans=min(ans,query(1,1,n,id[f1],id[x],xy));
    else ans=min(ans,query(1,1,n,id[l[xy]],id[x],xy));
    x=fa[f1]; f1=top[x];
  }
  x=xy;
  if (x!=1) dp[x]=ans; else dp[x]=0;
  change(1,1,n,id[x],x);
  ll u=head[x];
  while (u)
  {
    ll v=a[u].b;
    dfs3(v);
    u=a[u].a;
  }
/*  rep(i,0,ve1[x].size())
  {
    re xx=ve1[x][i];
    now[xx.a]=xx.b;
    if (xx.d!=-1)
    {
      ve[xx.a][xx.c]=xx.d;
    }
  } */
}
int main()
{
  freopen("1.in","r",stdin);
  freopen("1.out","w",stdout);
  read(n); read(m);
  rep(i,2,n)
  {
    ll x,y;
    read(x); read(y);
    arr(x,i,y);
    read(p[i]); read(q[i]); read(l[i]);
  }
  dfs1(1,0);
  dfs2(1,1);
  rep(i,1,19)
    rep(j,1,n)
    {
      bz1[i][j]=bz1[i-1][bz1[i-1][j]];
      bz2[i][j]=bz2[i-1][j]+bz2[i-1][bz1[i-1][j]];
    }
  rep(i,1,n)
  {
    ll ans=i;
    dep(j,19,0)
      if (l[i]>=bz2[j][ans]) l[i]-=bz2[j][ans],ans=bz1[j][ans];
    l[i]=max(ans,1);
  }
  dfs3(1);
  rep(i,2,n) printf("%lld\n",dp[i]);
  return 0; 
}

 

考虑一下理论复杂度更小的办法

现在我们需要的是一个支持撤销的操作

由于每次修改,我们只会修改栈顶位置和当前栈顶位置的元素

这个显然我们是可以通过vector记录来完成的

这样是nlog^2n的

然而 事实是 这个跑的比树剖

我也不是很理解为什么啊。。

常数应该就是2倍啊

难道开了o2vector还是那么慢啊??

树剖的log难道<2?? 

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define IL inline
#define rint register ll
#define rep(i,h,t) for (rint i=h;i<=t;i++)
#define dep(i,t,h) for (rint i=t;i>=h;i--)
IL ll max(ll x,ll y)
{
  if (x>y) return(x); else return(y);
}
IL ll min(ll x,ll y)
{
  if (x<y) return(x); else return(y); 
}
IL void swap(ll &x,ll &y)
{
  ll tmp=x; x=y; y=tmp;
}
char ss[1<<27],*A=ss,*B=ss;
IL char gc()
{
  return A==B&&(B=(A=ss)+fread(ss,1,1<<27,stdin),A==B)?EOF:*A++;
}
template<class T> void read(T &x)
{
  rint f=1,c; while (c=gc(),c<48||c>57) if (c=='-') f=-1; x=c^48;
  while (c=gc(),47<c&&c<58) x=(x<<3)+(x<<1)+(c^48); x*=f; 
}
#define mid ((h+t)/2)
//const ll N=6.3e5;
const int N2=2.3e5;
const ll INF=1e18;
ll now[N2*4],p[N2],q[N2],head[N2];
ll dep[N2],dis[N2],dp[N2],l[N2],lx,num;
int bz1[20][N2];
ll bz2[20][N2],n,m;
struct re{
  ll a,b,c,d;
}a[N2];
vector<ll> ve[N2*4];
vector<re> ve1[N2];
IL void arr(ll x,ll y,ll z)
{
  a[++lx].a=head[x];
  a[lx].b=y;
  a[lx].c=z;
  head[x]=lx;
}
void dfs1(ll x,ll y)
{
  ll u=head[x];
  //fa[x]=y; son[x]=-1; size[x]=1; 
  dep[x]=dep[y]+1;
  while(u)
  {
    ll v=a[u].b;
    dis[v]=dis[x]+a[u].c;
    dfs1(v,x);
    bz1[0][v]=x;
    bz2[0][v]=a[u].c;
    //size[x]+=size[v];
    //if (son[x]==-1||size[v]>size[son[x]]) son[x]=v;
    u=a[u].a;
  }
}
/* void dfs2(ll x,ll y)
{
  top[x]=y; id[x]=++num; real2[num]=x;
  if (son[x]==-1) return;
  dfs2(son[x],y);
  ll u=head[x];
  while (u)
  {
    ll v=a[u].b;
    if (v!=son[x]) dfs2(v,v);
    u=a[u].a;
  }
} */
IL double check1(ll x,ll y)
{
  return(1.00000000*(dp[x]-dp[y])/(dis[x]-dis[y]));
}
ll query(ll x,ll h,ll t,ll h1,ll t1,ll k)
{
  if (h1<=h&&t<=t1)
  {
    if (!now[x]) return(INF);
    ll l=0,r=now[x]-1;
    while (l<r)
    {
      ll midd=(l+r)/2;
      if (check1(ve[x][midd+1],ve[x][midd])<=p[k]) l=midd+1;
      else r=midd;
    }
    ll xx=ve[x][l];
    return(dp[xx]+p[k]*(dis[k]-dis[xx])+q[k]);
  }
  ll ans=INF;
  if (h1<=mid) ans=query(x*2,h,mid,h1,t1,k);
  if (mid<t1) ans=min(ans,query(x*2+1,mid+1,t,h1,t1,k));
  return(ans);
}
IL bool check2(ll x,ll y,ll z)
{
  if (1.00000000*(dp[z]-dp[x])/(dis[z]-dis[x])>=1.0000000*(dp[x]-dp[y])/(dis[x]-dis[y]))
    return(1); else return(0);
}
void change(ll x,ll h,ll t,ll pos,ll k)
{
  ll l=0,r=now[x]-1;
  while (l<r)
  {
    ll midd=(l+r)/2;
    if (check2(ve[x][midd+1],ve[x][midd],k)) l=midd+1;
    else r=midd;
  }
  /*ve1[k].push_back((re){x,now[x],l+1,l+1<now[x]?ve[x][l+1]:-1});
  if (!now[x]) now[x]=1; else now[x]=l+2;
  if (now[x]>ve[x].size()) ve[x].push_back(k); else ve[x][now[x]-1]=k;
  */ int tmp=now[x]; 
  if (!now[x]) now[x]=1; else now[x]=l+2;
  if (now[x]>ve[x].size()) 
    ve[x].push_back(k),ve1[k].push_back((re){x,tmp,-1,-1});
  else ve1[k].push_back((re){x,tmp,now[x]-1,ve[x][now[x]-1]}),ve[x][now[x]-1]=k;
  if (h==t) return;
  if (pos<=mid) change(x*2,h,mid,pos,k);
  else change(x*2+1,mid+1,t,pos,k);
}
void dfs3(ll x)
{
  ll ans=INF;
  /*vector<ll> ve2[N];
  int now2[N];
   for (int i=1;i<=n;i++) 
    for (int j=1;j<=now[i];j++)
      cout<<ve[i][j-1]<<" ";
  cout<<"              "<<x<<"a"<<endl; 
  for (int i=1;i<=n;i++)
    for (int j=1;j<=now[i];j++)
      ve2[i].push_back(ve[i][j-1]),now2[i]=now[i]; */
  ans=query(1,1,n,dep[l[x]],dep[x],x);
  if (x!=1) dp[x]=ans; else dp[x]=0; 
  change(1,1,n,dep[x],x);
  ll u=head[x];
  while (u)
  {
    ll v=a[u].b;
    dfs3(v);
    u=a[u].a;
  }
  rep(i,0,ve1[x].size()-1)
  {
    re xx=ve1[x][i];
    now[xx.a]=xx.b;
    if (xx.d!=-1)
    {
      ve[xx.a][xx.c]=xx.d;
    }
  }
/*
  for (int i=1;i<=n;i++) 
    for (int j=1;j<=now[i];j++)
      cout<<ve[i][j-1]<<" ";
  cout<<"              "<<x<<endl; */
  
 /* for (int i=1;i<=n;i++) 
    for (int j=1;j<=now[i];j++)
      if (ve[i][j-1]!=ve2[i][j-1]) 
        cout<<now[i]<<" "<<now2[i]<<"false"<<endl;*/
}
int main()
{
  freopen("1.in","r",stdin);
  freopen("1.out","w",stdout);
  read(n); read(m);
  rep(i,2,n)
  {
    ll x,y;
    read(x); read(y);
    arr(x,i,y);
    read(p[i]); read(q[i]); read(l[i]);
  }
  dfs1(1,0);
//  dfs2(1,1);
  rep(i,1,19)
    rep(j,1,n)
    {
      bz1[i][j]=bz1[i-1][bz1[i-1][j]];
      bz2[i][j]=bz2[i-1][j]+bz2[i-1][bz1[i-1][j]];
    }
  rep(i,1,n)
  {
    ll ans=i;
    dep(j,19,0)
      if (l[i]>=bz2[j][ans]) l[i]-=bz2[j][ans],ans=bz1[j][ans];
    l[i]=max(ans,1);
  }
  dfs3(1);
  rep(i,2,n) printf("%lld\n",dp[i]);
  return 0; 
}

 

另外一个做法是点分治

我觉得点分治挺有用的。。

首先点分治的一个结论是所有联通块的大小之和时nlogn级别的

然后我们取出重心之后,先做重心和根相连的这一块

然后对这些点建立凸包,用来更新其他点(点分治和cdq分治线段树分治这些都差不多,也就是利用了离线这一特性)

我们按照dep[i]-l[i]排序然后依次边建边询问

这样的复杂度是 nlogn(点数目)*logn(查询时间)

 

我记得apio的时候wys说这个有nlogn的做法 但我并不知道。。

 

posted @ 2018-05-30 20:52  尹吴潇  阅读(140)  评论(0编辑  收藏  举报