P2305 [NOI2014]购票
题目描述
详见:P2305 [NOI2014]购票
Solution
写出一个朴素的DP可以看出显然是一个斜率优化,且是在树上求解答案。
因此用点分治维护树上斜率优化DP。
设现在的重心为,根为,我们需要先递归处理所在的连通块。
接下来用 的路径上的值更新下面的点。
按这些点可以到达的最浅祖先的深度排序,依次加入新点,斜率优化维护,二分凸包求解答案即可。
时间复杂度 。
Code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=2e5+50;
const int INF=0x3f3f3f3f;
const ll loo=1ll<<60;
double slope[MAXN];
struct enode{ll to,nxt,c; } e[MAXN<<1];
ll edgenum=0,smin,root,num,top;
ll st[MAXN],size[MAXN],vis[MAXN],dis[MAXN],f[MAXN],E[MAXN],head[MAXN],p[MAXN],q[MAXN],l[MAXN],fa[MAXN];
inline ll read()
{ll x=0,f=1; char c=getchar();while (c<'0'||c>'9') { if(c=='-') f=-1; c=getchar(); }while (c>='0'&&c<='9') { x=(x<<3)+(x<<1)+(c^48); c=getchar(); }return x*f;
}
void add_edge(int u,int v,ll c) { e[++edgenum]=(enode){v,head[u],c}; head[u]=edgenum; }
void get_dis(int x)
{ for (int i=head[x];i!=-1;i=e[i].nxt) dis[e[i].to]=dis[x]+e[i].c,get_dis(e[i].to);
}
void get_root(int x,int Size)
{size[x]=1; ll mx=0;for (int i=head[x];i!=-1;i=e[i].nxt)if (!vis[e[i].to]){get_root(e[i].to,Size);size[x]+=size[e[i].to];mx=max(mx,size[e[i].to]);}mx=max(mx,Size-size[x]);if (smin>=mx) root=x,smin=mx;
}
void build(int x)
{E[++num]=x;for (int i=head[x];i!=-1;i=e[i].nxt) if (!vis[e[i].to]) build(e[i].to);
}
double slp(int x,int y){ return (1.0*f[y]-f[x])/(1.0*dis[y]-dis[x]); }
int compareE(int x,int y){ return dis[x]-l[x]>dis[y]-l[y]; }
void insert(int x)
{while (top>=2&&slope[top-1]<=slp(st[top],x)) top--;st[++top]=x,slope[top]=-1e18,slope[top-1]=slp(st[top-1],st[top]);
}
ll query(double x)
{ll l=1,r=top,ans;while (l<=r){ll mid=(l+r)>>1;if (slope[mid]<=x) ans=mid,r=mid-1;else l=mid+1;}return st[ans];
}
void solve(int x,int Size)
{if (Size==1) return;root=smin=INF;get_root(x,Size);ll rt=root,mx=smin;for (int i=head[rt];i!=-1;i=e[i].nxt) vis[e[i].to]=1,Size-=size[e[i].to];//cout<<Size<<" "<<rt<<" "<<mx<<endl;solve(x,Size);num=0;for (int i=head[rt];i!=-1;i=e[i].nxt) build(e[i].to);sort(E+1,E+num+1,compareE);top=0;for (int i=1,j=rt;i<=num;i++){while (j!=fa[x]&&dis[j]>=dis[E[i]]-l[E[i]]) insert(j),j=fa[j];if (top){int k=query(p[ E[i] ]);f[ E[i] ]=min(f[ E[i] ],f[k]+(dis[ E[i] ]-dis[k])*p[ E[i] ]+q[ E[i] ]);}}for (int i=head[rt];i!=-1;i=e[i].nxt) solve(e[i].to,size[e[i].to]);
}
int main()
{int n=read(),t=read();for (int i=1;i<=n;i++) head[i]=-1;for (int i=2;i<=n;i++) {int u=read(),c=read(); p[i]=read(),q[i]=read(),l[i]=read(),f[i]=loo;add_edge(u,i,c); fa[i]=u;} get_dis(1);solve(1,n);for (int i=2;i<=n;i++) printf("%lld\n",f[i]);return 0;
}