BZOJ3672 [Noi2014]购票

dp方程很好列:f[i]=min(f[j]+(d[i]-d[j])*p[i]+q[i]),j是i的祖先且d[i]-l[i]<=d[j].

考虑序列上的做法,这东西显然可以斜率优化,如果j<k且j比k优,则(f[j]-f[k])/(d[j]-d[k])>=p[i].

但是方程对j的范围有一个限制,所以我们可以考虑分治。

假设现在正在处理l~mid对mid+1~r的影响。

我们把mid+1~r按d[i]-l[i]从大到小排序,用l~mid建一个凸包,每次相当于从凸包头插入点,然后询问一个斜率的最优值,二分一下就行了。

现在这个东西到了树上,我们考虑树的点分治。

按重心分治,先递归有根的那边,之后把重心子树内的点取出,用根到重心这段的f值去更新,方法同上。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long ll;
const int N=200005,M=400005;
int n,e,si,mx,rt,tp,Tp,fa[N],sz[N],p[N],hd[N],nx[M],to[M],v[N],st[N],St[N];
ll y,q[N],l[N],w[M],d[N],f[N]; 
void ad(int x,int y,ll z) {to[++e]=y,w[e]=z,nx[e]=hd[x],hd[x]=e;}
bool cmp(int x,int y) {return d[x]-l[x]>d[y]-l[y];}

void dfs(int x,int fa) {
    sz[x]=1; int Mx=0;
    for(int i=hd[x];i;i=nx[i]) if(to[i]!=fa&&!v[to[i]]) dfs(to[i],x),sz[x]+=sz[to[i]],Mx=max(Mx,sz[to[i]]);
    Mx=max(Mx,si-sz[x]);
    if(Mx<mx) mx=Mx,rt=x;
}
void dfs2(int x,int fa) {sz[x]=1; for(int i=hd[x];i;i=nx[i]) if(to[i]!=fa&&!v[to[i]]) dfs2(to[i],x),sz[x]+=sz[to[i]];}
void dfs3(int x,int fa) {st[++tp]=x; for(int i=hd[x];i;i=nx[i]) if(to[i]!=fa&&!v[to[i]]) dfs3(to[i],x);}

double xl(int j,int k) {return (double)(f[j]-f[k])/(d[j]-d[k]);}
void mrg(int y,int x) {
    dfs2(x,0),v[x]=1;
    if(x!=1&&!v[fa[x]]) mx=si=sz[fa[x]],dfs(fa[x],0),mrg(y,rt);
    tp=Tp=0,dfs3(x,fa[x]),sort(st+1,st+1+tp,cmp);
    for(int i=1,u=fa[x];i<=tp;i++) {
        while(u!=fa[y]&&d[st[i]]-l[st[i]]<=d[u]) {
            while(Tp>1&&xl(St[Tp-1],St[Tp])<=xl(St[Tp],u)) Tp--;
            St[++Tp]=u,u=fa[u];
        }
        if(!Tp) continue;
        int l=1,r=Tp;
        while(l<r) {
            int m=(l+r)>>1;
            if(xl(St[m],St[m+1])<p[st[i]]) r=m; else l=m+1;
        }
        f[st[i]]=min(f[st[i]],f[St[l]]+(d[st[i]]-d[St[l]])*p[st[i]]+q[st[i]]);
    }
    for(int i=1;i<=tp;i++) if(st[i]!=x&&d[st[i]]-l[st[i]]<=d[x]) f[st[i]]=min(f[st[i]],f[x]+(d[st[i]]-d[x])*p[st[i]]+q[st[i]]);
    for(int i=hd[x];i;i=nx[i]) if(!v[to[i]]&&to[i]!=fa[x]) mx=si=sz[to[i]],dfs(to[i],0),mrg(to[i],rt);
}

int main() {
    scanf("%d%*d",&n),memset(f,0x3f,sizeof f),f[1]=0;
    for(int i=2;i<=n;i++) scanf("%d%lld%d%lld%lld",&fa[i],&y,&p[i],&q[i],&l[i]),ad(fa[i],i,y),ad(i,fa[i],y),d[i]=d[fa[i]]+y;
    si=mx=n,dfs(1,0),mrg(1,rt);
    for(int i=2;i<=n;i++) printf("%lld
",f[i]);
    return 0;
}
原文地址:https://www.cnblogs.com/juruolty/p/6695022.html