BZOJ3322: [Scoi2013]摩托车交易 MST 倍增

11 篇文章 0 订阅
4 篇文章 0 订阅

这不就是NOIP2013 DAY1 T3....

沙茶给跪啊,NOIP那题也没搞出来,去年省选完了也没管了。(桑心啊)。


鉴于SCOI2013极度坑爹不发数据题目都呈现半公开状态。

我还是发下题干吧。。


3322: [Scoi2013]摩托车交易

Time Limit: 5 Sec   Memory Limit: 128 MB
Submit: 72   Solved: 35
[ Submit][ Status]

Description

mzry1992 在打完吊针出院之后,买了辆新摩托车,开始了在周边城市的黄金运送生意。在mzry1992 生活的地方,城市之间是用双向高速公路连接的。另外,每条高速公路有一个载重上限,即在不考虑驾驶员和摩托车重量的情况下,如果所载货物的量超过某个值,则不能驶上该条高速公路。今年,mzry1992 一共收到了来自n 个不同城市的n 份定订单,每个订单要求卖出上限为一定量的黄金,或是要求买入上限为一定量的黄金。由于订单并不是同时发来的,为了维护生意上的名声,mzry1992 不得不按照订单发来的顺序与客户进行交易。他与第i 个客户进行交易的具体步骤是:
1. 前往第i 个客户所在城市。当然,中途是完全允许经过其他城市的。
2. 与第i 个客户进行交易,在此过程中他希望有限制的让交易额尽量大。具体的限制有两个
(a) 他希望与最后一个客户完成交易后,手上没有剩余黄金。
(b) 由于黄金是很贵重的物品,不能出现因为买入过多黄金而造成在以后的运送过程中不得不丢弃黄金的情况。
一开始,mzry1992 位于第一个订单客户所在的城市。现在有一个好消息,有人提供了mzry1992 免费试用周边城市的列车系统的资格。具体来讲,如果mzry1992希望从A 城市到达B 城市,且A、B 城市均有列车站的话,他可以携带着黄金与摩托车从A 城市乘坐列车到B 城市,这里假定乘坐列车没有载重限制。现在已知城市间的交通系统情况和订单情况,请帮助mzry1992 计算每个向mzry1992 购买黄金的客户的购买量。

Input

输入的第一行有三个整数n, m, q,分别表示城市数,连通城市的高速公路数和有列车站的城市数。

接下来的一行有n 个数,每个数均不相同,且值介于1 到n 之间,代表订单的顺序。

第三行有n 个数,第i 个数表示i 号城市的订单的上限额bi,bi 为正值表示该订单为买入交易(针对mzry1992 而言),上限为bi,bi 为负值表示该订单为卖出交易(同样针对mzry1992 而言)上限为 -bi。

接下来的m 行每行有三个数,u, v, w,表示城市u 和城市v 之间有一条载重上限为w 的高速公路,这里假定所有高速公路都是双向的,城市的序号是从1 到n 的。

输入的最后一行有q 个数,代表有列车站城市的序号。

对于20% 数据,n <= 100,m <= 200

对于50% 数据,n <= 3000,m <= 6000

对于100% 数据,1 <= n <= 10^5,n - 1 <= m <= 2*[1]10^5,0 <= q <= n,0 < |bi| < 10^9,0 < w < 10^9,保证任意两个城市之间是通过高速公路连通的。

Output

按照订单顺序对于每个卖出交易,输出一行,该行只有一个整数x,表示卖出黄金的量。

Sample Input

Sample #1
3 3 2
2 3 1
-6 5 -3
1 3 5
2 3 2
2 1 6
1 3

Sample #2
4 4 0
1 2 3 4
5 4 -6 -1
1 2 4
2 3 100
3 4 1
4 1 4

Sample Output

Sample #1
3
2

Sample #2
6
1
Hint
第一组样例:其中一种合法的方案是最初从2 号城市买入5 单位的黄金,先走第三条高速公路到1 号城市,然后再坐列车到3 号城市,在3 号城市卖出3 单位的黄金,然后乘坐列车到1 城市,在1 号城市卖出2 单位的黄金。

第二组样例:其中一种合法的方案是最初从1 号城市买入4 单位的黄金,走第一条高速公路,在2 号城市买入3 单位的黄金,走第二条高速公路,在三城市点卖出6 单位的黄金,走第三条高速公路,在4 号城市卖出1 单位的黄金。

HINT

应上传者要求,此系列试题不公开,如有异议,本站将删除之。



可以去UESTCOJ 提交:http://acm.uestc.edu.cn/#/problem/show/125 

当然BZOJ可以交,没有捐赠BZOJ的可以在这里提交


然后大致说一下过程:

比NOIP2013难一点的地方就是有列车站的点要缩起来。

然后直接最大生成树搞就行。

然后树上倍增记录最小值瓶颈边。

然后模拟即可....黄金能装就装。


虽然每个点黄金不超过10^9,但是有可能他连续在几个有列车站的城市买入导致暴INT。。(虽然不知道是否有这种坑爹数据)。

最好还是开LL。。


一份傻傻的代码:

#include <cstdio>
#include <algorithm>
#define rep(i,l,r) for (int i=l;i<=r;++i)
#define per(i,r,l) for (int i=r;i>=l;--i)
template<class T>bool upmin(T &a,T b){return a>b?a=b,1:0;}
template<class T> T Min(T a,T b){return a<b?a:b;}
typedef long long LL;
int getx(){
	char c;int x;bool pd=false;
	for (c=getchar();c!='-'&&(c<'0'||c>'9');c=getchar());
	if (c=='-') pd=true,c=getchar();
	for (x=0;c>='0'&&c<='9';c=getchar())
		x=(x<<3)+(x<<1)+c-'0';
	return pd?-x:x;
}
const LL INF=~0LLU>>1;
const int POW=16;
const int MAX_N=100500;
int n,m,q;
int order[MAX_N];
int b[MAX_N];
int bel[MAX_N];
int train[MAX_N];
struct Edge{int x,y,w;} e[MAX_N*2];
bool cmp(const Edge &a,const Edge &b){return a.w>b.w;}
void init(){
	n=getx(),m=getx(),q=getx();
	rep(i,1,n) order[i]=getx();
	rep(i,1,n) bel[i]=i,b[i]=getx();
	rep(i,1,m) e[i].x=getx(),e[i].y=getx(),e[i].w=getx();
	int minid=n;
	rep(i,1,q) train[i]=getx(),upmin(minid,train[i]);
	rep(i,1,q) bel[train[i]]=bel[minid];
}
int fa[MAX_N];
int getfa(int v){return fa[v]==v?v:fa[v]=getfa(fa[v]);}
int first[MAX_N],next[2*MAX_N],to[2*MAX_N],w[2*MAX_N],tal=0;
void tjb(int x,int y,int c){
	next[++tal]=first[x];
	to[first[x]=tal]=y;
	w[tal]=c;
}
void kruskal(){
	std::sort(e+1,e+m+1,cmp);
	rep(i,1,n) fa[i]=i;
	int last=n-(q<1?0:(q-1))-1;
	rep(i,1,m){
		int u=bel[e[i].x],v=bel[e[i].y];
		int A=getfa(u),B=getfa(v);
		if (A==B) continue;
		fa[A]=B,last--;
		if (u>v) u^=v^=u^=v;
		tjb(u,v,e[i].w);
		tjb(v,u,e[i].w);
		if (last==0) break;
		}
}
int deep[MAX_N];
int f[MAX_N][POW+1],g[MAX_N][POW+1];
int dfs(int v,int fa,int dep){
	deep[v]=dep;
	rep(i,1,POW) f[v][i]=f[f[v][i-1]][i-1];
	rep(i,1,POW) g[v][i]=Min(g[v][i-1],g[f[v][i-1]][i-1]);
	for (int k=first[v];k;k=next[k]){
		if (to[k]==fa) continue;
		f[to[k]][0]=v,g[to[k]][0]=w[k];
		dfs(to[k],v,dep+1);
		}
}
LL MinW(int x,int y){
	x=bel[x],y=bel[y];
	if (x==y) return INF;
	LL res=INF;
	if (deep[x]<deep[y]) x^=y^=x^=y;
	int delta=deep[x]-deep[y];
	if (delta) rep(i,0,POW) if (delta&(1<<i)){
		upmin(res,(LL)g[x][i]);
		x=f[x][i];
		}
	if (x!=y){
		per(i,POW,0) if (f[x][i]!=f[y][i]){
			upmin(res,(LL)Min(g[x][i],g[y][i]));
			x=f[x][i],y=f[y][i];
			}
		upmin(res,(LL)Min(g[x][0],g[y][0]));
		}
	return res;
}
void solve(){
	int Now=0,Pre=order[1];LL gold=0,sale=0;
	if (b[order[1]]>=0) gold+=b[order[1]];
		else printf("0\n");
	rep(i,2,n){
		Now=order[i];
		if (b[Now]>0) gold=Min(gold,MinW(Pre,Now))+(LL)b[Now];
		if (b[Now]<0){
			gold=Min(gold,MinW(Pre,Now));
			printf("%I64d\n",sale=Min(gold,(LL)-b[Now]));
			gold-=sale;
			}
		Pre=Now;
		}
}
int main(){
	init();
	kruskal();
	dfs(1,0,0);
	solve();
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值