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;
}
posted @ 2016-07-05 11:45  invoid  阅读(141)  评论(0编辑  收藏  举报