JZOJ3312-Luogu3280-摩托车交易

6 篇文章 0 订阅

Description&Data Constraint

mzry1992 在打完吊针出院之后,买了辆新摩托车,开始了在周边城市的黄金运送生意。

在mzry1992 生活的地方,城市之间是用双向高速公路连接的,另外,每条高速公路有一个载重上限,即在不考虑驾驶员和摩托车重量的情况下,如果所载货物的量超过某个值,则不能驶上该条高速公路。

今年,mzry1992 一共收到了来自n 个不同城市的n 份定订单,每个订单要求卖出上限为一定量的黄金,或是要求买入上限为一定量的黄金。由于订单并不是同时发来的,为了维护生意上的名声,mzry1992 不得不按照订单发来的顺序与客户进行交易。

他与第i 个客户进行交易的具体步骤是:

1.前往第i 个客户所在城市。当然,中途是完全允许经过其他城市的。

2.与第i 个客户进行交易。

但有两个限制:

(a) 与最后一个客户完成交易后,手上没有剩余黄金。
(b) 由于黄金是很贵重的物品,不能出现因为买入过多黄金而造成在以后的运送过程中不得不丢弃黄金的情况。

mzry希望总交易额最大,其次他希望卖出交易额序列的字典序最大。其中字典序指的是先比较第一次卖出交易额,若相等则比较下一次,以此类推。

一开始,mzry1992 位于第一个订单客户所在的城市。

现在有一个好消息,有人提供了mzry1992 免费试用周边城市的列车系统的资格。具体来讲,如果mzry1992希望从A 城市到达B 城市,且A、B 城市均有列车站的话,他可以携带着黄金与摩托车从A 城市乘坐列车到B 城市,这里假定乘坐列车没有载重限制。

现在已知城市间的交通系统情况和订单情况,请帮助mzry1992 计算每个向mzry1992 购买黄金的客户的购买量。

1 ≤ n ≤ 1 0 5 , n − 1 ≤ m ≤ 2 × 1 0 5 , 0 ≤ q ≤ n , 0 < ∣ b i ∣ < 1 0 9 , 0 < w < 1 0 9 1\le n\le 10^5,n-1\le m\le2\times 10^5,0\le q\le n,0<|bi|<10^9,0<w<10^9 1n105n1m2×1050qn0<bi<1090<w<109,保证任意两个城市之间是通过高速公路连通的。

Solution

要求总销售量最大,那么两点间的路径肯定要往大的走,那么跑一个最大生成树。铁路的话就相当于没有运载上限的公路,但是用 O ( n 2 ) O(n^2) O(n2)的时间连边显然不现实,所以设一个铁路总站,让每个铁路站点连向总站,上限为 inf ⁡ \inf inf

处理好图的问题后,在运输过程中,最后剩下的肯定是一条路径上的最小值,所以跑遍倍增求出最小值

考虑一种贪心,虽然题目要求不能丢弃,但是既然只求销售,我们可以在买的地方全买,某条路走不过就丢弃,到了最后也丢弃,因为你买了然后丢掉了相当于你一开始没买,对答案是不影响的

因此思路为

  • 求出整个图的最大生成树(包括铁路总站)
  • 按照顺序一个个走,遇到买的全买,遇到卖的能卖多少是多少
  • 走路径的途中遇到走不过的就丢掉
  • 遍历完所有就可求出答案

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define inf 891234567987654321
#define ll long long
#define N 100005
using namespace std;
struct krus
{
	int x,y;
	ll val;
}Krus[N*3];
struct node
{
	int head,next,to;
	ll val;
}a[N*6];
int n,m,q,x,y,tot,xx,yy,w[N],fa[N<<1][20],deep[N],father[N];
ll z,ans,c[N],f[N<<1][20];
bool cmp(krus x,krus y) {return x.val>y.val;}
int find(int x)
{
	if (father[x]!=x) father[x]=find(father[x]);
	return father[x];
}
void add(int x,int y,ll z)
{
	a[++tot].to=y;
	a[tot].val=z;
	a[tot].next=a[x].head;
	a[x].head=tot;
}
void dfs(int now,int Fa,ll val)
{
	deep[now]=deep[Fa]+1;
	fa[now][0]=Fa;
	f[now][0]=val;
	for (int i=a[now].head;i;i=a[i].next)
	{
		int u=a[i].to;
		ll v=a[i].val;
		if (u==Fa) continue;
		dfs(u,now,v);
	}
}
int LCA(int x,int y)
{
	if (deep[x]!=deep[y])
	{
		if (deep[x]<deep[y]) swap(x,y);
		for (int i=17;i>=0;--i)
		{
			if (deep[fa[x][i]]>deep[y]) x=fa[x][i];
		}
		x=fa[x][0];
	}
	if (x==y) return x;
	for (int i=17;i>=0;--i)
	{
		if (fa[x][i]!=fa[y][i])
		{
			x=fa[x][i];
			y=fa[y][i];
		}
	}
	return fa[x][0];
}
ll get(int x,int y)
{
	int lca=LCA(x,y);
	ll res=inf;
	for (int i=17;i>=0;--i)
	{
		if (deep[fa[x][i]]>deep[lca])
		{
			res=min(res,f[x][i]);
			x=fa[x][i];
		}
		if (deep[fa[y][i]]>deep[lca])
		{
			res=min(res,f[y][i]);
			y=fa[y][i];
		}
	}
	if (x!=lca) res=min(res,f[x][0]);
	if (y!=lca) res=min(res,f[y][0]);
	return res;
}
int main()
{
	scanf("%d%d%d",&n,&m,&q);
	for (int i=1;i<=n;++i)
		scanf("%d",&w[i]);
	for (int i=1;i<=n;++i)
		scanf("%lld",&c[i]);
	for (int i=1;i<=m;++i)
	{
		scanf("%d%d%lld",&x,&y,&z);
		Krus[i].x=x;Krus[i].y=y;Krus[i].val=z;
	}
	for (int i=1;i<=q;++i)
		scanf("%d",&x),Krus[m+i].x=x,Krus[m+i].y=n+1,Krus[m+i].val=inf;
	sort(Krus+1,Krus+m+q+1,cmp);
	for (int i=1;i<=n+1;++i)
		father[i]=i;
	for (int i=1;i<=m+q;++i)
	{
		x=Krus[i].x;y=Krus[i].y;
		xx=find(x);yy=find(y);
		if (xx!=yy)
		{
			father[xx]=yy;
			add(x,y,Krus[i].val);
			add(y,x,Krus[i].val);
		}
	}
	memset(fa,0,sizeof(fa));
	dfs(1,0,0);
	for (int j=1;j<=17;++j)
		for (int i=1;i<=n+1;++i)
		{
			fa[i][j]=fa[fa[i][j-1]][j-1];
			f[i][j]=min(f[i][j-1],f[fa[i][j-1]][j-1]);
		}
	ans=0;
	for (int i=1;i<=n;++i)
	{
		x=w[i];
		if (c[x]>0) ans+=c[x];
		else
		{
			printf("%lld\n",min(-c[x],ans));
			ans=max(ans+c[x],(ll)0);
		}
		if (i<n) ans=min(ans,get(w[i],w[i+1]));
	}
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值