[NOI2014]购票

[NOI2014]购票

题解

dp式子挺简单的。。直接列就完事了。。。
\(f(i)\) \(=\) \(q(i) + p(i) \cdot d(i) + min \{ f(j)-d(j) \cdot p(i) \}\)
如果是在数列上的话,这是一个比较容易的斜率优化dp。。
但是要注意这里的 \(p(i)\) 不是单调的,所以队列前端不能删除元素,只能每次询问都二分。。
这就导致在数列上也只能做到\(O(nlogn)\)
然后考虑树上的情况,一个节点可以有多个儿子,直接模拟队列是不可行的。。
我刚开始想的是按套路进行树剖,建成线段树,然后每次都能取出一个完整的队列询问。。
这样每次询问最多问到\(O(logn)\)条链,每条链对应的线段树上的点最多为\(O(logn)\),每个点取出队列来二分也是\(O(logn)\)..
总效率就是\(O(n{log}^{3}n)\)
看洛谷上有人说这样就能过了。。但是既然有两个log的算法那还是学一下两个log的吧。。
在树上进行点分治,每次找到重心后先把与根相连的部分先递归,然后再考虑与根相连的部分对重心的各个子树的贡献。。
总效率是\(O(n{log}^{2}n)\)

\(Code\)

#include <bits/stdc++.h>
#define LL long long
#define DB double
using namespace std;
const int N=2e5+10;
const LL INF=1e18;
int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void print(LL x){
    if(x>9) print(x/10);
    putchar(x%10+'0');
}
int n,op,cnt=0;
int fa[N],hed[N];
LL p[N],q[N],l[N],d[N],f[N];
bool vis[N];
struct edge{
    int r,nxt;
    LL w;
}e[N];
void insert(int u,int v,LL w){
    e[++cnt].r=v;e[cnt].nxt=hed[u];hed[u]=cnt;e[cnt].w=w;
}
void getd(int x){
    for(int i=hed[x];i;i=e[i].nxt){
        d[e[i].r]=d[x]+e[i].w;
        getd(e[i].r);
    }
}
int mx,rt;
int sz[N];
void getrt(int x,int S){
    sz[x]=1;int son=0;
    for(int i=hed[x];i;i=e[i].nxt)
        if(!vis[e[i].r]){
            getrt(e[i].r,S);
            sz[x]+=sz[e[i].r];
            son=max(son,sz[e[i].r]);
        }
    son=max(son,S-sz[x]);
    if(son<=mx){
        rt=x;
        mx=son;
    }
} 
int tp;
int st[N];
void getst(int x){
    st[++tp]=x;
    for(int i=hed[x];i;i=e[i].nxt) if(!vis[e[i].r]) getst(e[i].r);
}
bool cmp(int x,int y){
    return d[x]-l[x]>d[y]-l[y];
}
DB slope(int k,int j){
    return DB(f[k]-f[j])/(DB)(d[k]-d[j]);
}
int it;
int P[N];
void ins(int x){
    while(it>=2&&slope(P[it-1],P[it])<=slope(P[it],x)) --it;
    P[++it]=x;
}
int ask(LL ppp){
    int l=1,r=it,mid;
    while(l!=r){
        mid=l+r>>1;
        if(slope(P[mid],P[mid+1])>=ppp) l=mid+1;
        else r=mid;
    }
    return P[l];
}
void sol(int x,int S){
    if(S==1) return;
    mx=1e9;getrt(x,S);
    int y=rt;
    for(int i=hed[y];i;i=e[i].nxt) {
        vis[e[i].r]=1;S-=sz[e[i].r];
    }
    sol(x,S);
    tp=0;for(int i=hed[y];i;i=e[i].nxt) getst(e[i].r);
    sort(st+1,st+1+tp,cmp);
    it=0;int z=y;
    for(int i=1;i<=tp;++i){
        int v=st[i];
        while(z!=fa[x]&&d[z]>=d[v]-l[v]) {
            ins(z);z=fa[z];
        }
        if(it){
            int hhh=ask(p[v]);
            f[v]=min(f[v],f[hhh]+(d[v]-d[hhh])*p[v]+q[v]);
        }
    }
    for(int i=hed[y];i;i=e[i].nxt) sol(e[i].r,sz[e[i].r]);
}
int main(){
    LL w;
    scanf("%d%d",&n,&op);
    for(int i=2;i<=n;++i){
        scanf("%d%lld%lld%lld%lld",&fa[i],&w,&p[i],&q[i],&l[i]);
        insert(fa[i],i,w);f[i]=INF;
    }
    getd(1);sol(1,n);
    for(int i=2;i<=n;++i) printf("%lld\n",f[i]);
    return 0;
}
posted @ 2020-08-31 20:49  Iscream-2001  阅读(230)  评论(0编辑  收藏  举报
/* */