P2305 [NOI2014] 购票
洛谷csdn题解共存,未经许可请勿转载!
没事攻击一下自己的弱点: d p dp dp。
斜率优化dp啊,你有点明显了
这一题值得注意的不是要用斜率优化,而是要在树上求答案。
我一想,哎,麻烦,于是就把题目关了。。。。。。
这里给大家说一个简单一点的办法。
我们先设 G G G 为现在的重心,递归处理根 r o o t root root 所在的连通块,然后用从 G G G 到 r o o t root root 路径上 f f f 不断更新 G G G 下边的点。
按这些点可以到达的最浅祖先的深度排序,依次加入新点,斜率优化维护,二分凸包求解即可。
时间复杂度稳定保持 O ( n l o g 2 n ) O(n log2n) O(nlog2n)。
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;
}//获取根root
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]);//printf节省时间
return 0;
}