Codeforces 853B Jury Meeting (差分+前缀和)

<题目链接>

题目大意:

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

解题分析:

首先判断是否有至少长度为k的区间能够保证所有人能够来,并且回去。之后再利用差分、前缀和维护一定区间内所有人能够出发和到达的最小花费。

#include <bits/stdc++.h>
using namespace std;

#define pb push_back
#define mp make_pair
#define fi first
#define se second
const int N = 1e6+5;
typedef long long ll;
typedef pair<int,int> paii;
vector<paii> G[N],G1[N];
const ll INF = 1e15; 
ll preday[N],lastday[N];
int n,m,k;

bool cmp1(paii a,paii b){ return a.fi==b.fi?(a.se<b.se):(a.fi<b.fi); }
bool cmp2(paii a,paii b){ return a.fi==b.fi?(a.se<b.se):(a.fi>b.fi); }   //优先回来时间晚,花费少的航班

int main(){
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=m;i++){
        int day,u,v,cost;scanf("%d%d%d%d",&day,&u,&v,&cost);
        if(!v)G[u].pb(mp(day,cost));    //每个人出发的航班
        if(!u)G1[v].pb(mp(day,cost));   //每个人回来的航班
    }
    int l=-1,r=1e9;   //记录能够让所有人聚集在一起的最大区间长度
    for(int i=1;i<=n;i++){
        sort(G[i].begin(),G[i].end(),cmp1);
        sort(G1[i].begin(),G1[i].end(),cmp2);
        if(G[i].size())l=max(l,G[i][0].fi+1);
        if(G1[i].size())r=min(G1[i][0].fi-1,r);
        if(G[i].size()==0 || G1[i].size()==0)return 0*puts("-1");   //如果这个人不能出发或者回来
    }
    if(r-l+1<k || l==-1 || r==1e9)return 0*puts("-1");
    for(int i=1;i<=n;i++){
        ll now=INF;
        preday[1]+=now;
        for(int j=0;j<G[i].size();j++){   //从出发时间早的方案开始遍历。前缀求和之后,相当于只保留了(最早)的(最便宜)的方案的值
            if(G[i][j].se<now){
                preday[G[i][j].fi]-=now;
                preday[G[i][j].fi]+=G[i][j].se;
                now=G[i][j].se;
            }
        }//这里如果求前缀的话,sum_preday[day]就相当于得到第i个人在1~day天内出发所花费的最少钱数
        now=INF;
        lastday[int(1e6)]+=now;
        for(int j=0;j<G1[i].size();j++){
            if(G1[i][j].se<now){
                lastday[G1[i][j].fi]-=now;
                lastday[G1[i][j].fi]+=G1[i][j].se;
                now=G1[i][j].se;
            }
        }//这里如果求后缀,sum_lastday[day]就相当于得到第i个人day~1e6天内回来所花费的最少钱数
    }
    //计算1~n次之后,sum_preday[day]就表示n个人,在1~day天能够出发的最少钱数(如果他在day天之前能够出发的话,不能出发的话,他的前缀花费为INF)
    //后缀sum_lastday[day]同理
    for(int i=2;i<=int(1e6);i++)preday[i]+=preday[i-1];    //计算前缀     
    for(int i=int(1e6);i>=1;i--)lastday[i]+=lastday[i+1];  //计算后缀
    ll ans=INF;
    for(int i=l;i+k-1<=r;i++)
        ans=min(ans,preday[i-1]+lastday[i+k]);
    printf("%lld\n",ans);
}

 

 

2019-03-02

posted @ 2019-02-23 23:24  悠悠呦~  阅读(360)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end