bzoj3672: [Noi2014]购票
斜率优化+树分治。
点分治:找出当前子树的重心,分治根到重心这一段,更新根到重心这一段的值,将剩下的点按能到达的高度从低到高排序,更新。分治其他子树。
#include<cstdio> #include<algorithm> #include<cstring> #define LL long long using namespace std; const int maxn = 400000 + 10; const int maxm = 400000 + 10; int g[maxn],v[maxn],next[maxn],eid; LL w[maxn]; int n,m,tot,root,t; LL f[maxn],d[maxn],p[maxn],q[maxn],lim[maxn]; int fa[maxn],ms[maxn],size[maxn]; int seq[maxn],cnt; int stack[maxn],sp; double k[maxn]; bool mark[maxn]; void addedge(int a,int b,LL C) { v[eid]=b; w[eid]=C; next[eid]=g[a]; g[a]=eid++; } void dfs(int u) { for(int i=g[u];~i;i=next[i]) { d[v[i]]=d[u]+w[i]; dfs(v[i]); } } void get(int u) { ms[u]=0; size[u]=1; for(int i=g[u];~i;i=next[i]) if(!mark[i]) { get(v[i]); size[u]+=size[v[i]]; ms[u]=max(ms[u],size[v[i]]); } ms[u]=max(ms[u],tot-size[u]); if(ms[u]<ms[root]) root=u; } double slope(int x,int y) { return (double)(f[x]-f[y])/(double)(d[x]-d[y]); } void update(int x,int y) { if(d[x]-d[y]<=lim[x]) f[x]=min(f[x],f[y]+(d[x]-d[y])*p[x]+q[x]); } void visit(int u) { seq[++cnt]=u; for(int i=g[u];~i;i=next[i]) if(!mark[i]) visit(v[i]); } void insert(int x) { while(sp>1 && slope(x,stack[sp])>slope(stack[sp],stack[sp-1])) sp--; stack[++sp]=x; k[sp]=-slope(x,stack[sp-1]); } bool cmp(int x,int y) { return d[x]-lim[x]>d[y]-lim[y]; } void solve(int u) { if(tot<=1) return; root=0; get(u); int tmp=root; for(int i=g[fa[tmp]];~i;i=next[i]) if(!mark[i]&&v[i]==tmp) { mark[i]=1; tot=size[u]-size[v[i]]; solve(u); break; } for(int i=fa[tmp];i!=fa[u];i=fa[i]) update(tmp,i); cnt=0; for(int i=g[tmp];~i;i=next[i]) if(!mark[i]) visit(v[i]); sort(seq+1,seq+cnt+1,cmp); sp=0; for(int i=1,j=tmp;i<=cnt;i++) { int y=seq[i]; for(;j!=fa[u]&&d[j]>=d[y]-lim[y];j=fa[j]) insert(j); if(!sp) continue; else if(sp==1) update(y,stack[sp]); else update(y,stack[min(sp,(int)(upper_bound(k+2,k+sp+1,-p[y])-k-1))]); } for(int i=g[tmp];~i;i=next[i]) if(!mark[i]) { mark[i]=1; tot=size[v[i]]; solve(v[i]); } } int main() { memset(g,-1,sizeof(g)); scanf("%d%d",&n,&t); ms[0]=1000000000; for(int i=2,x,y;i<=n;i++) { scanf("%d%d%lld%lld%lld",&fa[i],&y,&p[i],&q[i],&lim[i]); addedge(fa[i],i,y); f[i]=1ll<<62; } dfs(1); tot=n; solve(1); for(int i=2;i<=n;i++) printf("%lld\n",f[i]); return 0; }