原题链接: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;
}