Codeforces Round #433Div. 2 D Jury Meeting 优先队列

16 篇文章 0 订阅

原题链接:http://codeforces.com/contest/854/problem/D

大致题意:n个人分别从1-n号城市乘飞机聚集到0号城市开会,开会时间为k天,每架航班都是当天起航,当天到达。只有所有人都已经到达0号城市后的第二天才能开会,开完k天会后的第二天每个人才能返回自己的城市。给出每架航班的出发时间,出发城市,抵达城市,价格,问所有人的最小总花费是多少。

我的想法是,既然给出的日期的范围只有10^6左右,那么就可以从小到大枚举开会的第一天,获得每个人在开会前一天达到的最小花费,同时用优先队列维护在开完后以后离开的最小代价。这样就可以得到把当前这一天作为开会第一天的最小花费,最后把所有可能的最小花费取最小值就是答案。

代码:

#include <bits/stdc++.h>
using namespace std;
inline void read(int &x){
    char ch;
    bool flag=false;
    for (ch=getchar();!isdigit(ch);ch=getchar())if (ch=='-') flag=true;
    for (x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());
    x=flag?-x:x;
}

inline void read(long long &x){
    char ch;
    bool flag=false;
    for (ch=getchar();!isdigit(ch);ch=getchar())if (ch=='-') flag=true;
    for (x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());
    x=flag?-x:x;
}

int const maxn = 120000 ;

long long n,m,k;
struct zy{
long long day,x,cost;
} A[maxn] , B[maxn];

int totA,totB;

struct kkk{
long long day ,cost;
};

bool cmp(zy A  , zy B){
return A.day < B.day ;
}

struct kkkkk{
deque<kkk> a;
int l=0,r=-1;

void push(kkk x){
while ( ( !a.empty() ) && ( x.cost <= a.back().cost ) )
    a.pop_back();
a.push_back ( x );
}

long long pop(long long lim){
long long tmp = ( *a.begin() ).cost;
while ( ( !a.empty() ) && ( ( *a.begin() ).day < lim ) )
    a.pop_front();
if (a.empty() )
    return -1;
return ( *a.begin() ).cost-tmp;
}

long long top(){
return ( *a.begin() ).cost;
}

bool Empty(){
return a.empty();
}


}leave[maxn];

long long Min[maxn];

bool vis[maxn];

int main(){
    read(n); read(m); read(k);
    for (int i=1;i<=m;i++)
        {
            long long a,b,c,d;
            read(a); read(b); read(c); read(d);
            zy tmp;
            tmp.day=a;
            tmp.x=b+c;
            tmp.cost=d;
            if ( c == 0 )
                A[++totA]=tmp;
            else
                B[++totB]=tmp;
        }
    sort( A+1 , A+1+totA ,cmp);
    sort( B+1 , B+1+totB ,cmp);
    int cnt=0;
    int stA=0;
    memset( Min , 63 ,sizeof (Min ) );
    for (int i = 1 ; i <= totA ; i++ )
    {
        Min[ A[i].x ]=min( Min[ A[i].x ] , A[i].cost );
        if ( !vis[ A[i].x ] )
            {
                vis[ A[i].x ]=1;
                cnt++;
                if (cnt==n)
                {
                    stA=i;
                    break;
                }
            }
    }
    if (cnt!=n)
        {
            puts("-1");
            return 0;
        }
    int stB=0;
    for (int i=1;i<=totB;i++)
        if ( B[i].day >= A[stA].day+k+1 )
            {
                stB=i;
                break;
            }

    for (int i=stB;i<=totB;i++)
        if ( B[i].day >= A[stA].day+k+1 )
        {
            kkk tmp;
            tmp.cost= B[i].cost;
            tmp.day= B[i].day;
            leave[ B[i].x ].push( tmp );
        }
    long long sum=0;
    for (int i=1;i<=n;i++)
        {
            if ( leave[ i ].Empty() )
                {
                    puts("-1");
                    return 0;
                }
            sum=sum+Min[i]+leave[ i ].top();
        }
    long long ans=sum;
    for (int i=1;i<=totA;i++)
        {
            int r=i-1;
            while ( ( r< totA )&& ( A[r+1].day == A[ i ].day ) )
            {
                r++;
                if ( Min[ A[r].x ] > A[r].cost )
                    {
                        sum+= A[r].cost - Min[ A[r].x ];
                        Min[ A[r].x ] = A[r].cost ;
                    }
            }
            i=r;
            while ( (stB<=totB ) && ( B[stB].day < A[i].day+k+1 ) )
                {
                    long long tmp=leave[ B[stB].x ].pop( A[i].day+k+1 );
                    if ( leave[ B[stB].x ] .Empty() )
                        break;
                    sum+=tmp;
                    stB++;
                }
            if ( leave[ B[stB].x ] .Empty() )
                break;
            ans=min(ans,sum);
        }
    printf("%I64d\n",ans);
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值