思路分析

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

显然我们希望城市之间的路径上的最小边权最大,即使多绕路也没有关系。于是我们可以想到先求出一棵最大生成树,用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. Python os.read() 方法

    概述 os.read() 方法用于从文件描述符 fd 中读取最多 n 个字节,返回包含读取字节的字符串,文件描述符 fd对应文件已达到结尾, 返回一个空字符串.高佣联盟 www.cgewang.com ...

  2. Skill 解决 Design Library 被识别成 Technology Library 的问题

    https://www.cnblogs.com/yeungchie/ code procedure(ycTechLibToDesign(libName attachLibName) prog((lib ...

  3. bzoj 3436小K的农场

    3436: 小K的农场 Time Limit: 10 Sec Memory Limit: 128 MB Submit: 2327 Solved: 1105 [Submit][Status][Discu ...

  4. Linux的VMWare14中配置Centos7桥接网络环境(网络一)

    1.查看当前初始环境如下:在windows端先查看本机ip   ifconfig

  5. 3月21日考试 题解(数据结构+区间DP+贪心)

    前言:T3写挂了,有点难受. --------------- T1 中位数 题意简述:给你一段长度为$n$的序列,分别输出$[1,2k-1]$的中位数$(2k-1\leq n)$. --------- ...

  6. 025_go语言中的通道同步

    代码演示 package main import "fmt" import "time" func worker(done chan bool) { fmt.P ...

  7. 阿里ECS云服务器部署文件

    今天,接触了阿里ECS云服务器,免费领取链接https://dwz.cn/WOFZpZz1 获取之后,要添加一下端口,刚开始需要80  8080  3306的端口,其他的根据需要自行添加 点击快速创建 ...

  8. SpringCloud微服务:基于Nacos组件,整合Dubbo框架

    源码地址:GitHub·点这里 || GitEE·点这里 一.基础组件简介 1.Dubbo框架 Dubbo服务化治理的核心框架,之前几年在国内被广泛使用,后续由于微服务的架构的崛起,更多的公司转向微服 ...

  9. C#LeetCode刷题之#389-找不同(Find the Difference)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/4062 访问. 给定两个字符串 s 和 t,它们只包含小写字母. ...

  10. [算法入门]——深度优先搜索(DFS)

    深度优先搜索(DFS) 深度优先搜索叫DFS(Depth First Search).OK,那么什么是深度优先搜索呢?_? 样例: 举个例子,你在一个方格网络中,可以简单理解为我们的地图,要从A点到B ...