「题解」「CF853B」Jury Meeting

题目

传送门

思路

十分巧妙的差分前缀和好题。

题目板块完结之后,我看到有很多处理此题的方法,但总感觉差分前缀和比较巧妙。

首先,通过输入我们可以将每个人能在 \(0\) 号点停留的最大时间区间 \([tl,tr]\) ,并将所有人的 \([tl,tr]\) 取交集,得到 \([ll,rr]\) 即表示只有在这个区间中所有人能够聚集在一起。

显然,如果 \(rr-ll-1<k\) 则直接 puts("-1") 即可。

然后怎么办?假设所有人聚集的时间从 \(i\) 开始到 \(i+k+1\) 或者更久,那么我们可以保证他们工作的时常至少为 \(k\) ,那么我们需要找的就是所有人在 \([1,i]\) 时间段内到 \(0\) 的最小花费以及 \([i+k+1,n]\) 时间段返回的最小花费之和。

两种情况是一样的,我们只讨论其中一种,不妨讨论前往 \(0\) 的情况。

对于一个人,我们假设他在 \(i\) 时刻到达 \(0\) 最便宜的价格为 \(m_i\),再将他的所有前往 \(0\) 的航班按时间顺序由小到大排序。

规定:\(t_i\) 表示第 \(i\) 趟航班起飞的时间,\(c_i\) 为其花费,\(m_i\) 为这个人在 \(i\) 时刻到达 \(0\) 的最小花费。

现在我们分析他的第一堂航班和第二趟航班,由于 \(t\) 我们已经按序排序了,现在讨论 \(c\) 的大小关系:

  1. \(c_1\le c_2\) ,那么这个人坐第一趟航班肯定是最便宜的,而且我们可以对 \(m_i\) 数组进行修改,即 \(\forall m_i,t_1\le i<t_3,m_i=c_1\) ,注意,此处 \(i<t_3\) 而非 \(t_2\) (仔细想想,为什么?);
  2. \(c_1>c_2\) ,此刻我们发现,在时间 \(i\) 介于区间 \([t_1,t_2)\) 的时候,肯定只能乘坐第一趟航班,但如果 \(i\ge t_2\) ,我们乘坐第二趟航班无疑最优,即当 \(t_1\le i < t_2\) 时,\(m_i=c_1\) ,当 \(i\le t_2\) 时,\(m_i=c_2\)

将我们的分析推广到整个体系当中:

在枚举当前是第 \(i\) 趟航班时,保留 \(1,2,\ldots i-1\) 趟航班中的最小花费 \(\min\)

  • 如果 \(c_i<\min\) ,那么我们更新 \(\min\) ,并用 \(c_i\)\(m_i,i\in [t_i,t_{i+1})\) 全部更新;
  • 如果 \(c_i\ge \min\) ,那么我们保留 \(\min\) ,并用 \(\min\)\(m_i,i\in [t_i,t_{i+1}]\) 全部更新;

至于从 \(0\) 返回希望大家自行分析,因为代码中有一些 \(+1\) 如果没有分析是很难弄懂的。

经过分析,我们发现这个操作十分像区间赋值,那么这里就有许多数据结构和思想值得我们使用:

  • 线段树
  • 树状数组
  • 差分前缀和

个人推荐差分前缀和,因为前两者的修改、询问都是 \(\mathcal O(\log N)\) 的,后者修改 \(\mathcal O(1)\) ,询问 \(\mathcal O(\log N)\),而基于我们的分析似乎修改操作十分繁杂,而询问操作只有在最后计算答案时使用。

那么我们对于每一个人,可以开一个 \(ansl[i][t]\) 表示这个人 \(i\)\(t\) 时刻到 \(0\) ,再开一个 \(ansr[i][t]\) 表示这个人再 \(t\) 时刻离开 \(0\) 。但是人有 \(10^5\) 个,时间刻度有 \(10^6\) 个,开二维肯定不限时,但由于人与人之间相互独立,没有什么影响,我们考虑所有人共用一个 \(ansl\) 和一个 \(ansr\) 数组。

具体细节见代码。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<vector>
#include<utility>
using namespace std;

#define rep(i,__l,__r) for(signed i=__l,i##_end_=__r;i<=i##_end_;++i)
#define fep(i,__l,__r) for(signed i=__l,i##_end_=__r;i>=i##_end_;--i)
#define writc(a,b) fwrit(a),putchar(b)
#define mp(a,b) make_pair(a,b)
#define ft first
#define sd second
#define LL long long
#define ull unsigned long long
#define uint unsigned int
#define pii pair< int,int >
#define Endl putchar('\n')
#define CODEFAIL puts("-1"),exit(0)
// #define FILEOI
// #define int long long
// #define int unsigned

#ifdef FILEOI
# define MAXBUFFERSIZE 500000
    inline char fgetc(){
        static char buf[MAXBUFFERSIZE+5],*p1=buf,*p2=buf;
        return p1==p2&&(p2=(p1=buf)+fread(buf,1,MAXBUFFERSIZE,stdin),p1==p2)?EOF:*p1++;
    }
# undef MAXBUFFERSIZE
# define cg (c=fgetc())
#else
# define cg (c=getchar())
#endif
template<class T>inline void qread(T& x){
    char c;bool f=0;
    while(cg<'0'||'9'<c)f|=(c=='-');
    for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    if(f)x=-x;
}
inline int qread(){
    int x=0;char c;bool f=0;
    while(cg<'0'||'9'<c)f|=(c=='-');
    for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    return f?-x:x;
}
// template<class T,class... Args>inline void qread(T& x,Args&... args){qread(x),qread(args...);}
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}
inline int gcd(const int a,const int b){return b?gcd(b,a%b):a;}
inline void getInv(int inv[],const int lim,const int MOD){
    inv[0]=inv[1]=1;for(int i=2;i<=lim;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
}
template<class T>void fwrit(const T x){
    if(x<0)return (void)(putchar('-'),fwrit(-x));
    if(x>9)fwrit(x/10);
    putchar(x%10^48);
}
inline LL mulMod(const LL a,const LL b,const LL mod){//long long multiplie_mod
    return ((a*b-(LL)((long double)a/mod*b+1e-8)*mod)%mod+mod)%mod;
}

const int MAXN=1e5;
const int MAXM=1e5;
const int MAXK=1e6;
const int INF=(1<<30)-1;

int n,m,k,ll,rr;
vector< pii >a[MAXN+5],b[MAXN+5];
//a[i] : 第 i 个点到 0 点的时间, 花费
//b[i] : 0 点到 i 点的时间, 花费

inline void input(){
    n=qread(),m=qread(),k=qread();
    for(int i=1,d,f,t,c;i<=m;++i){//照常规输入即可
        d=qread(),f=qread(),t=qread(),c=qread();
        if(f==0)b[t].push_back(mp(d,c));
        else a[f].push_back(mp(d,c));
    }
}

inline void init(){
    ll=-1,rr=INF;
    int tl,tr;
    rep(i,1,n){
        sort(a[i].begin(),a[i].end());
        sort(b[i].begin(),b[i].end());
        tl=INF,tr=-1;
        if(!a[i].empty())tl=a[i].begin()->first;
        if(!b[i].empty())tr=b[i].back().first;
        if(tl==INF || tr==-1)CODEFAIL;//如果连来的机票或者回去的机票的没有, 直接 gg
        ll=Max(ll,tl),rr=Min(rr,tr);//取交集
        // printf("i == %d, tl == %d, tr == %d\n",i,tl,tr);
    }
    // printf("ll == %d, rr == %d\n",ll,rr);
    if(ll==-1 || rr==INF || rr-ll-1<k)CODEFAIL;//如果缺机票或者全部人最大的交集都不够 k 天也 gg 了
}

LL ansl[MAXK+5],ansr[MAXK+5],res=(1ll<<60)-1;
inline void solve(){
    for(int i=1,now,cost;i<=n;++i){
        now=1,cost=INF;//初始化
        // puts("For into ansl");
        for(int t=0,siz=a[i].size(),tmp;t<siz;++t){
            // printf("When i == %d, now == %d, cost == %d\n",i,now,cost);
            tmp=a[i][t].first;
            ansl[tmp]-=cost;
            ansl[now]+=cost;
            //差分前缀和的基本操作
            //注意:此处有别于下面, 原因在于这里是处理前往 0 点而非离开 0 点
            now=tmp,cost=Min(cost,a[i][t].second);
            // printf("After i == %d, now == %d, cost == %d\n",i,now,cost);
        }
        ansl[MAXK+1]-=cost;
        ansl[now]+=cost;
        now=MAXK,cost=INF;//倒着处理
        for(int t=b[i].size()-1,tmp;t>=0;--t){
            tmp=b[i][t].first;
            ansr[now+1]-=cost,ansr[tmp+1]+=cost;
            now=tmp,cost=Min(cost,b[i][t].second);
        }
        ansr[1]+=cost,ansr[now+1]-=cost;
        // printf("After i == %d, the two arr:\n",i);
        // rep(t,1,20)writc(ansl[t],' ');Endl;
        // rep(t,1,20)writc(ansr[t],' ');Endl;
    }
    rep(i,1,MAXK)ansl[i]+=ansl[i-1],ansr[i]+=ansr[i-1];//差分前缀和数组的最后一步
    // rep(i,1,20)writc(ansl[i],' ');
    // Endl;
    // rep(i,1,20)writc(ansr[i],' ');
    // Endl;
    for(int i=ll;i+k+1<=rr;++i)res=Min(res,ansl[i]+ansr[i+k+1]);
    writc(res,'\n');
}

signed main(){
#ifdef FILEOI
    freopen("file.in","r",stdin);
    freopen("file.out","w",stdout);
#endif
    input();
    init();
    solve();
    return 0;
}

似乎有点贪心?

posted @ 2020-02-10 16:36  Arextre  阅读(165)  评论(0编辑  收藏  举报