「NOI2014」购票

「NOI2014」购票

解题思路

先列出 \(dp\) 式子并稍微转化一下
\[ dp[u] =\min(dp[v]+(dis[u]-dis[v]) \times p[u] + q[u])) \ \ \ \ (dis[v]-lim[u] \leq dis[u]) \\ dp[u]=\min(dp[v]+dis[v]\times p[u]) + p[u]\times dis[u]+q[u] \\ \]

假设有 \(dis(v2)< dis(v1)\)\(p(u)\)\(v2\) 的取值比 \(v1\) 优,可以得到斜率式
\[ dp(v2)+p(u)\times dis(v2)\leq dis(v1)+p(u)\times dp(v1) \\ \dfrac{dp(v2) -dp(v1)}{dis(v2)-dis(v1)}\geq p(u) \]
利用 \(cdq\) 的思想对有根树进行点分治,每次计算分治中心 \(H\) 到其祖先的一条链对其它联通块的贡献,维护一个凸包在凸包上二分即可,还挺好写的,复杂度 \(\mathcal O(n\log^2n)\)

code
/*program by mangoyang*/ 
#include<bits/stdc++.h>
#define inf ((ll)(1e18))
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
    int ch = 0, f = 0; x = 0;
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
    for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
    if(f) x = -x;
}
#define int ll
const int N = 2000005;
vector<int> g[N];
int dis[N], dep[N], lim[N], dp[N], d[N], Q[N], p[N], pa[N];
int A[N], sz[N], vis[N], pq[N], q[N], n, mn, rt, all, cnt;
inline double slope(int x, int y){ 
    return (double) (dp[y] - dp[x]) / (double) (dis[y] - dis[x]);
}
inline bool cmp(int x, int y){
    return dis[x] - lim[x] > dis[y] - lim[y];
}
inline void update(int x, int y){
    dp[x] = min(dp[y] + (dis[x] - dis[y]) * p[x] + q[x], dp[x]);
}
inline void prework(int u, int fa){
    pa[u] = fa, dep[u] = dep[fa] + 1, dis[u] = dis[fa] + d[u];
    for(int i = 0; i < (int) g[u].size(); i++)
        if(g[u][i] != fa) prework(g[u][i], u);
}
inline void getsize(int u, int fa){
    int mson = 0; sz[u] = 1;
    for(int i = 0; i < (int) g[u].size(); i++){
        int v = g[u][i];
        if(vis[v] || v == fa) continue;
        getsize(v, u), sz[u] += sz[v];
        if(sz[v] > mson) mson = sz[v];
    }
    mson = max(mson, all - sz[u]);
    if(mson < mn) mn = mson, rt = u;
}
inline void dfs(int u, int fa){
    A[++cnt] = u;
    for(int i = 0; i < (int) g[u].size(); i++)
        if(!vis[g[u][i]] && g[u][i] != fa) dfs(g[u][i], u);
}
inline void divtree(int u, int top){
    vis[u] = 1; int lst = all;
    for(int i = 0; i < (int) g[u].size(); i++){
        int v = g[u][i];
        if(!vis[v] && v == pa[u]){
            mn = all = sz[v] > sz[u] ? lst - sz[u] : sz[v];
            getsize(v, u), divtree(rt, top);
        }
    }   
    for(int s = pa[u]; s != pa[top]; s = pa[s]) 
        if(dis[u] - dis[s] <= lim[u]) update(u, s);
    cnt = 0;
    for(int i = 0; i < (int) g[u].size(); i++){
        int v = g[u][i];
        if(!vis[v]) dfs(v, u);
    }
    sort(A + 1, A + cnt + 1, cmp);
    int t = 0; int s = u;
    for(int i = 1; i <= cnt; i++){
        while(s != pa[top] && dis[A[i]] - lim[A[i]] <= dis[s]){
            while(t > 1 && slope(Q[t-1], Q[t]) <= slope(Q[t-1], s)) t--;
            Q[++t] = s, s = pa[s];
        }
        int l = 1, r = t - 1, res = 1;
        while(l <= r){
            int mid = (l + r) >> 1;
            if(slope(Q[mid], Q[mid+1]) >= (double) p[A[i]]) 
                res = l = mid + 1;
            else r = mid - 1;
        }
        if(res <= t) update(A[i], Q[res]);
    }
    for(int i = 0; i < (int) g[u].size(); i++){
        int v = g[u][i];
        if(vis[v]) continue;
        mn = all = sz[v] > sz[u] ? lst - sz[u] : sz[v];
        getsize(v, u), divtree(rt, v);
    }
}
signed main(){
    read(n); int type; read(type);
    for(int i = 2, x; i <= n; i++){
        read(x), read(d[i]), read(p[i]);
        read(q[i]), read(lim[i]), dp[i] = inf;
        g[x].push_back(i), g[i].push_back(x);
    }
    prework(1, 0);
    mn = all = n, getsize(1, 0), divtree(rt, 1);
    for(int i = 2; i <= n; i++) printf("%lld\n", dp[i]);
    return 0;
}

转载于:https://www.cnblogs.com/mangoyang/p/10519355.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值