思路分析

为了让交易额尽量大,显然我们需要尽量多地买入。对于每个城市,到达这个城市时携带的黄金受到几个条件的影响:之前卖出的黄金,之前能买入的最多的黄金,前一个城市到当前城市的路径上的最小边权。既然不需要输出买入的数量,我们可以先尽量多地买入,然后再按照边权的限制削减。这样,刚好卖完的限制也就没有影响了。卖出时当然也要尽量都,因此卖出的量就是前一个城市能带到当前城市的最多的黄金和卖出限制中小的一个。

显然我们希望城市之间的路径上的最小边权最大,即使多绕路也没有关系。于是我们可以想到先求出一棵最大生成树,用kruskal就可以了。

然后我们需要知道最大生成树上两点之间的距离,这个距离可以用倍增或者树剖来求。

对于有列车站的城市,可以看作同一个城市。注意,买入和卖出的限制还是要按照原城市算,在求最大生成树和进行树剖处理询问时才合并。这里用有列车站的编号最小的城市来代表这些城市的代表编号。

整理一下思路,对于每个城市,我们执行以下操作:
1. 求出前一个城市最多能带到当前城市多少黄金
2. 若卖出,则输出黄金量和卖出限制中小的一个,并维护黄金量;若买入,则直接当做全部买入,维护黄金量即可。

具体实现

1. 建一棵最小生成树

跑一遍kruskal即可,就不再赘述了。

struct Edge
{
int x,y,z;
#define x(i) e[i].x
#define y(i) e[i].y
#define z(i) e[i].z
}e[2*M]; void add(int x,int y,int z)
{
ver[++tot]=y,edge[tot]=z,Next[tot]=head[x],head[x]=tot;
ver[++tot]=x,edge[tot]=z,Next[tot]=head[y],head[y]=tot;
} bool cmp(Edge a,Edge b)
{
return a.z>b.z;
} int get(int a)
{
return fa[a]==a?a:fa[a]=get(fa[a]);
} void kruskal()
{
sort(e+1,e+m+1,cmp);
for(int i=1;i<=n;i++)
fa[i]=i;
for(int i=1;i<=m;i++)
{
int x=p[x(i)]?minp:x(i),y=p[y(i)]?minp:y(i),fx,fy;//看看能否合并
fx=get(x),fy=get(y);
if(fx==fy)
continue;
fa[fx]=fy;
add(x,y,z(i));
}
}

2. 树剖预处理

正常的树剖预处理,这里用每条边两个端点中深度更大的节点存储这条边的权值,方便处理。对树链剖分不熟悉的可以点击食用

struct Tree
{
int siz,f,d,seg,son,top;
#define siz(i) t[i].siz
#define f(i) t[i].f
#define d(i) t[i].d
#define seg(i) t[i].seg
#define son(i) t[i].son
#define top(i) t[i].top
}t[N]; struct Seg
{
int l,r,rev;
ll sum;
#define l(i) c[i].l
#define r(i) c[i].r
#define sum(i) c[i].sum
#define rev(i) c[i].rev
}c[N]; void dfs1(int f,int x)
{
siz(x)=1,d(x)=d(f)+1,f(x)=f;
for(int i=head[x];i;i=Next[i])
if(ver[i]!=f)
{
int y=ver[i];
dfs1(x,y);
siz(x)+=siz(y);
if(siz(y)>siz(son(x)))
son(x)=y;
len[y]=edge[i];//用深度较大的端点存储边的权值
}
} void dfs2(int x)
{
if(son(x))
{
seg(son(x))=++seg(0);
rev(seg(0))=son(x);
top(son(x))=top(x);
dfs2(son(x));
}
for(int i=head[x];i;i=Next[i])
if(!top(ver[i]))
{
int y=ver[i];
seg(y)=++seg(0);
rev(seg(0))=y;
top(y)=y;
dfs2(y);
}
} void build(int p,int l,int r)
{
l(p)=l,r(p)=r;
if(l==r)
{
sum(p)=len[rev(l)];
return ;
}
int mid=(l+r)>>1;
build(lson,l,mid),build(rson,mid+1,r);
sum(p)=min(sum(lson),sum(rson));
} seg(1)=++seg(0),rev(1)=1,top(1)=1,len[1]=IINF;

3. 依次对每个城市进行处理

先算出上一个城市到当前城市最多能携带多少黄金,若卖出,则取携带量和卖出限制中小的一个卖出;若买入,则全部买入。

有一点需要注意的,因为是用深度较大的端点存储边的权值,因此查询的时候不能查询到LCA处,因为LCA存储的边权不在路径上。

ll op(int x,int y)
{
ll val=IINF;
x=p[x]?minp:x,y=p[y]?minp:y;//看看能否合并
while(top(x)!=top(y))
{
if(d(top(x))<d(top(y)))
swap(x,y);
val=min(val,ask(1,seg(top(x)),seg(x)));
x=f(top(x));
}
if(d(x)>d(y))
swap(x,y);
val=min(val,ask(1,seg(x)+1,seg(y)));//不能查询到LCA处
return val;
} void solve()
{
for(int i=1;i<=n;i++)
{
now=min(i!=1?op(rank[i-1],rank[i]):IINF,now);//查询上一个城市到当前城市的路径上最小边权
if(w[rank[i]]<0)
{
printf("%lld\n",min(-w[rank[i]],now));//卖出较小的一个
now-=min(-w[rank[i]],now);//维护剩下的黄金
}
else
now+=w[rank[i]];//全部买入
}
}

最后奉上完整代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define lson p<<1
#define rson p<<1|1
#define ll long long
using namespace std;
const int N=1e6,M=1e6,INF=1e8;
const ll IINF=1e17;
int n,m,q,tot,minp=INF;
ll now;
int head[N],ver[2*M],Next[2*M];
int rank[N],fa[N];
ll edge[2*M];
ll w[N],len[N];
bool p[N];
struct Edge
{
int x,y,z;
#define x(i) e[i].x
#define y(i) e[i].y
#define z(i) e[i].z
}e[2*M];
struct Tree
{
int siz,f,d,seg,son,top;
#define siz(i) t[i].siz
#define f(i) t[i].f
#define d(i) t[i].d
#define seg(i) t[i].seg
#define son(i) t[i].son
#define top(i) t[i].top
}t[N];
struct Seg
{
int l,r,rev;
ll sum;
#define l(i) c[i].l
#define r(i) c[i].r
#define sum(i) c[i].sum
#define rev(i) c[i].rev
}c[N];
bool cmp(Edge a,Edge b)
{
return a.z>b.z;
}
void add(int x,int y,int z)
{
ver[++tot]=y,edge[tot]=z,Next[tot]=head[x],head[x]=tot;
ver[++tot]=x,edge[tot]=z,Next[tot]=head[y],head[y]=tot;
}
void dfs1(int f,int x)
{
siz(x)=1,d(x)=d(f)+1,f(x)=f;
for(int i=head[x];i;i=Next[i])
if(ver[i]!=f)
{
int y=ver[i];
dfs1(x,y);
siz(x)+=siz(y);
if(siz(y)>siz(son(x)))
son(x)=y;
len[y]=edge[i];
}
}
void dfs2(int x)
{
if(son(x))
{
seg(son(x))=++seg(0);
rev(seg(0))=son(x);
top(son(x))=top(x);
dfs2(son(x));
}
for(int i=head[x];i;i=Next[i])
if(!top(ver[i]))
{
int y=ver[i];
seg(y)=++seg(0);
rev(seg(0))=y;
top(y)=y;
dfs2(y);
}
}
void build(int p,int l,int r)
{
l(p)=l,r(p)=r;
if(l==r)
{
sum(p)=len[rev(l)];
return ;
}
int mid=(l+r)>>1;
build(lson,l,mid),build(rson,mid+1,r);
sum(p)=min(sum(lson),sum(rson));
}//2. 树剖预处理
ll ask(int p,int l,int r)
{
if(l<=l(p) && r(p)<=r)
return sum(p);
int mid=(l(p)+r(p))>>1;
ll val=IINF;
if(l<=mid)
val=min(val,ask(lson,l,r));
if(r>mid)
val=min(val,ask(rson,l,r));
return val;
}
int get(int a)
{
return fa[a]==a?a:fa[a]=get(fa[a]);
}
void kruskal()
{
sort(e+1,e+m+1,cmp);
for(int i=1;i<=n;i++)
fa[i]=i;
for(int i=1;i<=m;i++)
{
int x=p[x(i)]?minp:x(i),y=p[y(i)]?minp:y(i),fx,fy;
fx=get(x),fy=get(y);
if(fx==fy)
continue;
fa[fx]=fy;
add(x,y,z(i));
}
}//1. 建一棵最小生成树
ll op(int x,int y)
{
ll val=IINF;
x=p[x]?minp:x,y=p[y]?minp:y;
while(top(x)!=top(y))
{
if(d(top(x))<d(top(y)))
swap(x,y);
val=min(val,ask(1,seg(top(x)),seg(x)));
x=f(top(x));
}
if(d(x)>d(y))
swap(x,y);
val=min(val,ask(1,seg(x)+1,seg(y)));
return val;
}
void read()
{
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++)
scanf("%d",&rank[i]);
for(int i=1;i<=n;i++)
scanf("%lld",&w[i]);
for(int i=1;i<=m;i++)
scanf("%d%d%d",&x(i),&y(i),&z(i));
for(int i=1;i<=q;i++)
{
int x;
scanf("%d",&x);
p[x]=1;minp=min(minp,x);
}
}
void solve()
{
for(int i=1;i<=n;i++)
{
now=min(i!=1?op(rank[i-1],rank[i]):IINF,now);
if(w[rank[i]]<0)
{
printf("%lld\n",min(-w[rank[i]],now));
now-=min(-w[rank[i]],now);
}
else
now+=w[rank[i]];
}
}//3. 依次对每个城市进行处理
int main()
{
read();
kruskal();
seg(1)=++seg(0),rev(1)=1,top(1)=1,len[1]=IINF;
dfs1(0,1),dfs2(1),build(1,1,n);
solve();
return 0;
}

[SCOI2013]摩托车交易 题解的更多相关文章

  1. BZOJ3322[Scoi2013]摩托车交易——最大生成树+贪心+倍增

    题目描述 mzry1992 在打完吊针出院之后,买了辆新摩托车,开始了在周边城市的黄金运送生意.在mzry1992 生活的地方,城市之间是用双向高速公路连接的.另外,每条高速公路有一个载重上限,即在不 ...

  2. 【[SCOI2013]摩托车交易 】

    倍增什么的最慢了,常数太大了 我们可以上树剖啊 但是如果用树剖来查询树上两点之间的最小边权的话,可能只能在上一棵线段树? 那也太\(naive\)了,尽管倍增常数大,但是还是比两个\(log\)快的 ...

  3. [SCOI2013]摩托车交易 kruskal重构树(最大生成树) 倍增

    ---题面--- 题解: 这题想法简单,,,写起来真的是失智,找了几个小时的错误结果是inf没开到LL范围.... 首先我们需要找到任意两点之间能够携带黄金的上限值,因为是在经过的道路权值中取min, ...

  4. BZOJ3322 : [Scoi2013]摩托车交易

    求出最大生成树,则两点间的最大容量为树上两点间的边权的最小值. 设$lim[i]$表示第$i$个订单的城市允许携带的黄金上限,则 $lim[i]=\min(lim[i+1],a[i]和a[i+1]点间 ...

  5. 2019.03.28 bzoj3322: [Scoi2013]摩托车交易(kruskal重构树+贪心)

    传送门 题意咕咕咕 思路: 先把所有可以列车通的缩成一个点,然后用新图建立kruskalkruskalkruskal重构树. 这样就可以倒着贪心模拟了. 代码: #include<bits/st ...

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

    题目描述 mzry1992 在打完吊针出院之后,买了辆新摩托车,开始了在周边城市的黄金运送生意.在mzry1992 生活的地方,城市之间是用双向高速公路连接的.另外,每条高速公路有一个载重上限,即在不 ...

  7. bzoj AC倒序

    Search GO 说明:输入题号直接进入相应题目,如需搜索含数字的题目,请在关键词前加单引号 Problem ID Title Source AC Submit Y 1000 A+B Problem ...

  8. BZOJ3323: [Scoi2013]多项式的运算

    3323: [Scoi2013]多项式的运算 Time Limit: 12 Sec  Memory Limit: 64 MBSubmit: 128  Solved: 33[Submit][Status ...

  9. toodifficult 题解

    名字听起来十分厉害啊...一道lzz的提交答案题. 提答题,我们看看题目,给出一个解密程序,叫你加密. 每个点有一个加密的sample和一些要加密的文本. 从题目中我们可以得到一些信息: 加密后一般为 ...

随机推荐

  1. JS中Math.random()的使用和扩展

    Math.random()方法返回大于等于 0 小于 1 的一个随机数.对于某些站点来说,这个方法非常实用,因为可以利用它来随机显示一些名人名言和新闻事件. 在连续整数中取得一个随机数 值 = Mat ...

  2. Kylin Flink Cube 引擎的前世今生

    Apache Kylin™ 是一个开源的.分布式的分析型数据仓库,提供 Hadoop/Spark 之上的 SQL 查询接口及多维分析(OLAP)能力以支持超大规模数据,它能在亚秒内查询巨大的表. Ky ...

  3. ARC 062 F - Painting Graphs with AtCoDeer 割点 割边 不动点 burnside引理

    LINK:Painting Graphs with AtCoDeer 看英文题面果然有点吃不消 一些细节会被忽略掉. 问每条边都要被染色 且一个环上边的颜色可以旋转. 用c种颜色有多少本质不同的方法. ...

  4. fiddler本地调试

    参考:https://blog.csdn.net/letasian/article/details/75021656 有关fiddler基础用法的介绍详见我的上一篇博客:http://www.cnbl ...

  5. WebLogic 省略项目名称

    希望 WebLogic 部署的项目,不需要输入项目名,直接通过IP端口访问. 在 WEB-INF 目录下添加文件 weblogic.xml <?xml version="1.0&quo ...

  6. 【LeetCode/LintCode】 题解丨字节跳动试题:第k大的子数组

    给定一个长度为n的数组a,它有n(n+1)/2​​个子数组.请计算这些子数组的和,然后按照升序排列,并返回排序后第k个数. 1≤n≤10​^5 1≤a​i≤10^​9 1≤k≤​n(n+1)/2 在线 ...

  7. tableau用户留存分析

    1.数据源 这是个母婴产品的购买流水数据 2.数据处理 字段拆分.创建购买点会员生命周期 3.分析不同省份的留存率情况 根据第12个月的留存率对省市进行分组 实际业务中也可以通过类似的方法对用户年龄组 ...

  8. 2020-06-16:Redis hgetall时间复杂度?

    福哥答案2020-06-16: 时间复杂度是O(N).时间复杂度:O(N) where N is the size of the hash.

  9. 2020-04-14:mysql原子性和持久性怎么保证

    1.Mysql怎么保证一致性的? OK,这个问题分为两个层面来说. 从数据库层面,数据库通过原子性.隔离性.持久性来保证一致性.也就是说ACID四大特性之中,C(一致性)是目的,A(原子性).I(隔离 ...

  10. Android 开发学习进程0.16 layout_weight属性 R文件关联XML Module

    layout_weight属性 layout_weight属性我们常常用到,但有时候会发现它还有一些奇怪的属性,比如大多数使用时会把宽度设置成0,但要是宽度不设置成0会有什么效果? layout_we ...