SCOI2013摩托车交易 - 最大生成树+树链剖分

Posted lrj124

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SCOI2013摩托车交易 - 最大生成树+树链剖分相关的知识,希望对你有一定的参考价值。

题目描述

mzry1992 在打完吊针出院之后,买了辆新摩托车,开始了在周边城市的黄金运送生意。在mzry1992 生活的地方,城市之间是用双向高速公路连接的。另外,每条高速公路有一个载重上限,即在不考虑驾驶员和摩托车重量的情况下,如果所载货物的量超过某个值,则不能驶上该条高速公路。

今年,mzry1992 一共收到了来自n 个不同城市的n 份定订单,每个订单要求卖出上限为一定量的黄金,或是要求买入上限为一定量的黄金。由于订单并不是同时发来的,为了维护生意上的名声,mzry1992 不得不按照订单发来的顺序与客户进行交易。他与第i 个客户进行交易的具体步骤是:
1.前往第i 个客户所在城市。当然,中途是完全允许经过其他城市的。
2.与第i 个客户进行交易,在此过程中他希望有限制的让交易额尽量大。具体的限制有两个
(a) 他希望与最后一个客户完成交易后,手上没有剩余黄金。
(b) 由于黄金是很贵重的物品,不能出现因为买入过多黄金而造成在以后的运送过程中不得不丢弃黄金的情况。

一开始,mzry1992 位于第一个订单客户所在的城市。现在有一个好消息,有人提供了mzry1992 免费试用周边城市的列车系统的资格。具体来讲,如果mzry1992希望从A 城市到达B 城市,且A、B 城市均有列车站的话,他可以携带着黄金与摩托车从A 城市乘坐列车到B 城市,这里假定乘坐列车没有载重限制。
现在已知城市间的交通系统情况和订单情况,请帮助mzry1992 计算每个向mzry1992 购买黄金的客户的购买量。

思路

首先,经过的路径一定在最大生成树上。
其次,不需要考虑交易后剩余黄金的情况,因为可以根据客户的需求而调整买入多少。
这样,跑一遍最大生成树,再用树剖维护树上路径最小值,来看买入多少。

代码

/************************************************
*Author        :  lrj124
*Created Time  :  2019.10.10.21:11
*Mail          :  1584634848@qq.com
*Problem       :  luogu3280
************************************************/
#include <algorithm>
#include <cstring>
#include <cstdio>
#define int ll
using ll = long long;
using namespace std;
const ll inf = 10000000000000ll;
const int maxn = 100000 + 10;
int egcnt,n,m,q,cnt,per[maxn],dep[maxn],father[maxn],top[maxn],num[maxn],val[maxn],Map[maxn],siz[maxn],son[maxn],fir[maxn];
struct Edge { int to,nex; ll w; } eg[maxn];
ll minv[maxn<<2],w[maxn];
struct Road {
    int u,v;
    ll w;
    inline bool operator < (Road cmp) {
        return w > cmp.w;
    }
} edge[maxn*3];
inline void addedge(int u,int v,ll w) {
    eg[++egcnt] = {v,fir[u],w}; fir[u] = egcnt;
    eg[++egcnt] = {u,fir[v],w}; fir[v] = egcnt;
}
inline void dfs1(int now,int fa) {
    siz[now] = 1;
    father[now] = fa;
    dep[now] = dep[fa]+1;
    for (int i = fir[now];~i;i = eg[i].nex)
        if (eg[i].to ^ fa) {
            dfs1(eg[i].to,now);
            val[eg[i].to] = eg[i].w;
            siz[now] += siz[eg[i].to];
            if (siz[son[now]] < siz[eg[i].to]) son[now] = eg[i].to;
        }
}
inline void dfs2(int now,int ntop) {
    num[now] = ++cnt;
    Map[cnt] = now;
    top[now] = ntop;
    if (son[now]) dfs2(son[now],ntop);
    for (int i = fir[now];~i;i = eg[i].nex)
        if (eg[i].to ^ father[now] && eg[i].to ^ son[now]) dfs2(eg[i].to,eg[i].to);
}
inline void pushup(int root) { minv[root] = min(minv[root<<1],minv[root<<1|1]); }
inline void build(int l,int r,int root) {
    if (l == r) {
        minv[root] = val[Map[l]];
        return;
    }
    int mid = l+r>>1;
    build(l,mid,root<<1);
    build(mid+1,r,root<<1|1);
    pushup(root);
}
inline ll query(int l,int r,int ql,int qr,int root) {
    if (l > qr || r < ql) return inf;
    if (ql <= l && r <= qr)  return minv[root];
    int mid = l+r>>1;
    return min(query(l,mid,ql,qr,root<<1),query(mid+1,r,ql,qr,root<<1|1));
}
inline ll query_edge(int u,int v) {
    ll ans = inf;
    for (;top[u] ^ top[v];u = father[top[u]]) {
        if (dep[top[u]] < dep[top[v]]) swap(u,v);
        ans = min(ans,query(1,n,num[top[u]],num[u],1));
    }
    if (dep[u] > dep[v]) swap(u,v);
    return min(ans,query(1,n,num[u]+1,num[v],1));
}
inline int find(int x) { return father[x] = father[x] == x ? x : find(father[x]); }
inline void kruskal() {
    for (int i = 1;i <= n;i++) father[i] = i;
    if (!q) q = 1;
    int cnt = 0;
    sort(edge+1,edge+m+q);
    for (int i = 1,x,y;i <= m+q-1;i++) {
        x = find(edge[i].u);
        y = find(edge[i].v);
        if (x ^ y) {
            father[x] = y;
            addedge(edge[i].u,edge[i].v,edge[i].w);
            cnt++;
        }
        if (cnt == n-1) break;
    }
    memset(father,0,sizeof(father));
}
signed main() {
//  freopen("luogu3280.in","r",stdin);
//  freopen("luogu3280.out","w",stdout);
    memset(fir,-1,sizeof(fir));
    scanf("%lld%lld%lld",&n,&m,&q);
    for (int i = 1;i <= n;i++) scanf("%lld",&per[i]);
    for (int i = 1;i <= n;i++) scanf("%lld",&w[i]);
    for (int i = 1;i <= m;i++) scanf("%lld%lld%lld",&edge[i].u,&edge[i].v,&edge[i].w);
    for (int i = 1,x,y;i <= q;i++) { scanf("%lld",&x); if (i ^ 1) edge[m+i-1] = {x,y,inf}; y = x; }
    kruskal();
    dfs1(1,0);
    dfs2(1,1);
    build(1,n,1);
    ll now = 0;
    for (int i = 1;i < n;i++)
        if (w[per[i]] > 0) now = min(now+w[per[i]],query_edge(per[i],per[i+1]));
        else {
            printf("%lld
",min(-w[per[i]],now));
            now = min(max(0ll,now+w[per[i]]),query_edge(per[i],per[i+1]));
        }
    if (w[per[n]] < 0) printf("%lld",min(-w[per[n]],now));
    return 0;
}

以上是关于SCOI2013摩托车交易 - 最大生成树+树链剖分的主要内容,如果未能解决你的问题,请参考以下文章

bzoj 4568: [Scoi2016]幸运数字树链剖分+线段树+线性基

BZOJ 4568 4568: [Scoi2016]幸运数字 (线性基+树链剖分+线段树)

UVA 11354 - Bond (最小生成树 + 树链剖分)

洛谷P4180 [Beijing2010组队]次小生成树Tree(最小生成树,LCT,主席树,倍增LCA,倍增,树链剖分)

Bzoj2164 采矿(线段树+树链剖分)

树链剖分小结