思路分析

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

显然我们希望城市之间的路径上的最小边权最大,即使多绕路也没有关系。于是我们可以想到先求出一棵最大生成树,用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. loj #6247. 九个太阳 k次单位根 神仙构造 FFT求和原理

    LINK:九个太阳 不可做系列. 构造比较神仙. 考虑FFT的求和原理有 \(\frac{1}{k}\sum_{j=0}^{k-1}(w_k^j)^n=[k|n]\) 带入这道题的式子. 有\(\su ...

  2. 省选模拟赛day4

    怎么说?发现自己越来越菜了 到了不写题解写不出来题目的地步了.. 这次题目我都有认真思考 尽管思考的时候状态不太好 但是 我想 再多给我时间也思考不出来什么吧 所以写一份题解. T1 n个点的有根树 ...

  3. 【SDOI2012】Longge 的问题 题解(欧拉函数)

    前言:还算比较简单的数学题,我这种数学蒟蒻也会做QAQ. --------------- 题意:求$\sum\limits_{i=1}^n gcd(i,n)$的值. 设$gcd(i,n)=d$,即$d ...

  4. Android SQLite轻量级数据库(简单介绍)

    SQLite它是相当于嵌入到安卓里的一个小数据库吧, 它也可以使用SQL语句进行数据库的增删改查操作,但是是SQL1992的语句. 然后SQLite也有自己的语句,但是学过SQL的应该都会发现,它比较 ...

  5. 004_自己尝试go语言中的方法

    go语言可以给任意类型定义方法,我在学习过程中,一开始一头雾水,但是随着理解的深入,现在也大概知道了什么叫做方法 之前的一些例子其实讲的并不是特别生动,下面我用一个生动的例子演示一下 首先提出需求.我 ...

  6. 解决:HBuilder X 未检测到手机或模拟器

    1.问题 我使用Android 9版本的手机,开发者选项已开启USB调试,但是HBuilderX未检测到手机或模拟器. 2.解决办法 1.找到HBuilderX安装目录下的D:\Archive\HBu ...

  7. DIFF算法浅析(三)在react中的实现

    在虚拟dom中diff的实现. 分别从4个方面: DIFF抽象概念(概述.时间复杂性分析) 在Vue2中的实现(版本2.6.11.必要性.执行方式) 在React中的实现(版本16.13.1,必要性. ...

  8. Markdown上手

    Markdown 学习日记 标题 二级标题 两个#+空格 快捷键:Ctrl + 2 三级标题 三个#+空格 快捷键:Ctrl + 3 最多支持六级标题 字体 加粗 文字 两个 *+文字+两个 * 快捷 ...

  9. Grazing on the Run 题解

    [题目大意] 大致题意就是,你的初始坐标为\(x\),你要去数轴上的\(n\)个点,问你到达所有点的时间总和最小是多少. 直接贪心肯定不行,所以考虑\(DP\) 先把坐标离散(也就是预处理两点距离\( ...

  10. java 基本类型包装类

    一 基本类型包装类 1.包装类概述 Java中提供了相应的对象来解决实现字符串与基本数据之间转换问题,基本数据类 型对象包装类:java将基本数据类型值封装成了对象. 8种基本类型对应的包装类如下: ...