Codeforces 853B Round #433 Div2D& Div1B Jury Meeting:差分前缀和+模拟

题意:有n(<=1e5)个城市和一个首都(0号城市),现在每个城市有一个选手,总共有m(<=1e5)次航班,每个航班要么从首都起飞,要么飞到首都去。每个飞机当天飞当天到。且坐飞机这一天什么也不能干,只能等飞机。每个飞机有一个花费和起飞时间。现在要把所有人集中到首都k(<=1e6)天,然后让他们各自回家。求最小花费,如果不可能实现k天或者不能回家了。或者去不了首都等等都输出-1。


题解:每个人可以先处理出一个极大可行区间,就是说我在[ dl , dr ]这段时间可以在首都呆着(保证有飞机接送),就是区间∪。然后所有人的区间取∩就是大家都在首都的最大可行区间了。如果有解,这个区间长度必须大于k。当获得了这个区间的时候,也就知道了所有的可行方案。假如一个方案是在start以及之前到达,在start+k+1以及之后离开。那么每个人显然应该选择[1 , start]时间内最便宜的飞机 和 [start+k+1 , 1e6]时间内最便宜的飞机。那么显然。。我们只需要对每个人把去和回的飞机遍历完一遍,就可以逐段确定答案了。那么这个任务交给前缀和用差分来搞。然后剩下的任务就是在可行区间内枚举答案了。


思路挺easy的。。。但是我这个菜鸡比赛时候一个多小时都没写好。。。。抠脚


Code:

#include<bits/stdc++.h>
using namespace std;
const int MAX = 1e5+100;
const int MAXK = 1e6+100;
typedef long long LL;
int m,n,k;
bool solved = false;
vector<pair<int,int> > a[MAX],b[MAX];
bool cmp(const pair<int,int> a,const pair<int,int>b){
	return a.first<b.first;
} 
LL ansl[MAXK],ansr[MAXK];
LL res;
int ll,rr;
void input(){
	scanf("%d%d%d",&n,&m,&k);
	for (int i=1;i<=m;i++){
		int d,f,t,c;
		scanf("%d%d%d%d",&d,&f,&t,&c);
		if (f==0){
			b[t].push_back(make_pair(d,c));
		}else{
			a[f].push_back(make_pair(d,c));
		} 
	}
}
void init(){
	ll = -1;
	rr = 0x3f3f3f3f;
	for (int i=1;i<=n;i++){
		sort(a[i].begin(),a[i].end(),cmp);
		sort(b[i].begin(),b[i].end(),cmp);
		int dl = 0x3f3f3f3f;
		int dr = -1;
		for (vector<pair<int,int> >::iterator it = a[i].begin();it!=a[i].end();++it){
			dl = min(dl,it->first);
		}
		for (vector<pair<int,int> >::iterator it = b[i].begin();it!=b[i].end();++it){
			dr = max(dr,it->first);
		}
		if (dl==0x3f3f3f3f||dr==-1){
			solved = true;
			printf("-1\n");
			return;
		}
		ll = max (ll,dl);
		rr = min(rr,dr);
	}
	if (ll==-1||rr==0x3f3f3f3f){
		solved = true;
		printf("-1\n");
	}
}
void solve(){
	if (solved){
		return;
	}
	if (rr-ll-1<k){
		printf("-1\n");
		return ;
	}
	memset(ansl,0,sizeof ansl);
	memset(ansr,0,sizeof ansr);
	res = 0x3f3f3f3f3f3f3f3fLL;
	for (int i=1;i<=n;i++){
		int now = 1;
		int cost = 0x3f3f3f3f;
		for (int t = 0;t<a[i].size();++t){
			int temp = a[i][t].first;
//			cout<<"start "<<now<<" "<<temp<<" "<<cost<<endl;
			ansl[temp]-=cost;
			ansl[now]+=cost;
			now = temp;
			cost = min(cost,a[i][t].second);
		}
		ansl[(int)(1e6)+1]-=cost;
		ansl[now]+=cost;
		now =1e6;
		cost = 0x3f3f3f3f;
		for (int t = b[i].size()-1;t>=0;--t){
			int temp = b[i][t].first;
//			cout<<"end "<<temp<<" "<<now<<" "<<cost<<endl; 
			ansr[now+1]-=cost;
			ansr[temp+1]+=cost;
			now = temp;
			cost = min(cost,b[i][t].second);
		}
		ansr[1]+=cost;
		ansr[now+1]-=cost;
	}
	for (int i=1;i<=1e6;i++){
		ansl[i]+=ansl[i-1];
		ansr[i]+=ansr[i-1];
	} 
	for (int start = ll;start+k+1<=rr;start++){
		res = min(res,ansl[start]+ansr[start+k+1]);
//		cout<<"ans "<<start<<" "<<start+k+1<<" : "<<ansl[start]<<" "<<ansr[start+k+1]<<endl;
	}
	printf("%I64d\n",res);
}
int main(){
//	freopen("output.txt","w",stdout);
	input();
	init();
	solve();
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值