CF 845D Jury Meeting 枚举断点+前缀后缀

题意:n+1个city,city[0]为中心,其余n个city每个city都住一人,有m条边进出city[0].
边参数为:d[i],f[i],t[i],c[i]表示该边可以在第d[i]天使用 从f[i]飞到t[i]代价为c[i](f[i]或t[i]=0)
n,m<=1e5,k,d[i]<=1e6,问n个city的人都到中心并且同时在中心开会k天后各自返回需要的最小代价?无解输出-1
 
枚举开会时间为第t天 则第i个人只要在[1..t-1]天达到(选这些天内到0航班的最小值).然后再[t+k+1,1e6]选最小的回家即可.


直接一天一天找显然是不可能的 m<=1e5 则将航线按时间排序后,枚举取的时候的断点i 表示去首都0的票都在i之前.

对此维护前缀i,n个人买票的最小值,后缀也是如此.最后枚举端点后 二分第一个大于a[i].day的后缀即可.O(mlogm)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e6+20;
const ll inf=2e16;
ll n,m,k;
struct node{
	ll d,from,to,val;
}a[N];
bool cmp(node a,node b){return a.d<b.d;}
ll pre[N],suf[N],sum=0,res=0,mk[N];
int main()
{
	cin>>n>>m>>k;
	for(int i=1;i<=m;i++)
		scanf("%I64d%I64d%I64d%I64d",&a[i].d,&a[i].from,&a[i].to,&a[i].val);
	sort(a+1,a+1+m,cmp);
	memset(pre,-1,sizeof(pre));
	memset(suf,-1,sizeof(suf));
	memset(mk,0,sizeof(mk));
	for(int i=1;i<=m;i++)
	{
		if(a[i].from==0)	continue;
		if(mk[a[i].from]==0)
			res+=a[i].val,sum++,mk[a[i].from]=a[i].val;
		else
		{
			if(mk[a[i].from]>a[i].val)
				res-=mk[a[i].from],mk[a[i].from]=a[i].val,res+=a[i].val;	
		}	
		if(sum==n)
			pre[i]=res;
	}
	res=sum=0;
	memset(suf,-1,sizeof(suf));
	memset(mk,0,sizeof(mk));
	for(int i=m;i>=1;i--)
	{
		int u=a[i].to;
		if(a[i].from!=0)	continue;
		if(mk[u]==0)
			sum++,res+=a[i].val,mk[u]=a[i].val;
		else if(mk[u]>a[i].val)
			res-=mk[u],mk[u]=a[i].val,res+=a[i].val;
		if(sum==n)
		{
			suf[i]=res;
			if(suf[i+1]!=-1)
				suf[i]=min(suf[i],suf[i+1]);
		}
	}
	ll ans=inf;
	for(int i=1;i<=m;i++)
	{
		if(pre[i]==-1)	continue;
		ll day=a[i].d+k+1;
		ll l=i+1,r=m,pos=-1;
		while(l<=r)
		{
			int mid=l+r>>1;
			if(a[mid].d>=day)
				pos=mid,r=mid-1;
			else
				l=mid+1;
		}
		if(pos==-1)	continue;
		if(suf[pos]!=-1)
			ans=min(ans,suf[pos]+pre[i]);
	}
	if(ans>=inf)
		ans=-1;
	printf("%I64d\n",ans);
	return 0;
} 


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值