LOJ4218 「IOI2024」尼罗河船运 题解

题目描述

\(n\) 件手工艺品,第 \(i\) 件重量为 \(w_i\) ,有参数 \(a_i\)\(b_i\)

每艘船最多可以运输两件手工艺品:

  • 如果只运输第 \(i\) 件,重量没有要求,代价为 \(a_i\)
  • 如果同时运输第 \(i\) 和第 \(j\) 件,要求 \(|w_i-w_j|\le D\) ,代价 \(b_i+b_j\)

\(q\) 次询问,给定 \(D\) ,求运输所有物品的最小总代价。

数据范围

  • \(1\le n,q\le 10^5\)
  • \(1\le b_i\lt a_i\le 10^9,1\le w_i,D\le 10^9\)

时间限制 \(\texttt{2s}\) ,空间限制 \(\texttt{2048MB}\)

分析

令初始代价为 \(\sum_{i=1}^n a_i\) ,则第 \(i\) 件商品合运可以节约 \(c_i=a_i-b_i\) 的代价。

将所有商品按 \(w_i\) 排序,假设合运的下标集合为 \(s_1,\cdots,s_{2k}\) ,显然让 \(s_1\)\(s_2\) 合运,\(\cdots\)\(s_{2k-1}\)\(s_{2k}\) 合运最优。

\(f_i\) 表示仅考虑前 \(i\) 件商品,最多能节约的代价。转移方程为:

\[f_i=\max(f_{i-1},\max_{w_j\ge w_i-D}f_{j-1}+c_j+c_i)\\ \]

合法的 \(j\) 是一段后缀,单调队列维护 \(f_{j-1}+c_j\) 的最大值即可做到 \(\mathcal O(qn)\) ,可以通过前 \(5\) 个子任务。

观察子任务 \(6\) ,此时 \(c_i\) 全为 \(1\) 。按照 \(D\) 升序扫描所有询问,将相邻重量差 \(\le D\) 的位置连起来,则答案为 \(\sum\lfloor\) 连续段长度 \(/2\rfloor\)

对于一般情况,如果连续段长度为偶数,那么我们全盘保留;如果连续段长度为奇数,那么我们丢且仅丢一个商品。

容易发现丢商品的限制与下标奇偶性有关,比如说,假设连续段开头下标为奇数,那么下标为奇数的商品可以无条件丢弃(左右都是偶数个商品,刚好完成配对),但下标为偶数的商品要求 \(w_{i+1}-w_{i-1}\le D\) 才能丢弃(因为第 \(i-1\) 和第 \(i+1\) 个商品会配对)。

在扫描 \(D\) 的过程中,对连续段内奇偶下标分别维护 \(c_i\) 最小值和满足 \(w_{i+1}-w_{i-1}\le D\)\(c_i\) 最小值,动态更新答案即可。

由于只会进行 \(\mathcal O(n)\) 次连续段合并,所以时间复杂度 \(\mathcal O(n+q)\)

#include<bits/stdc++.h>
#include"nile.h"
#define ll long long
#define vi vector<int>
#define fi first
#define se second
#define mp make_pair
#define pii pair<int,int>
using namespace std;
const int maxn=1e5+5,inf=1e9+5;
int f[maxn],w[maxn];
pii d[maxn],p[maxn],r[maxn],s[maxn];
struct node
{
    int len,val;
    int mn[2][2]={inf,inf,inf,inf};///mn[x][0/1]表示连续段中奇偶性为x,无/有限制时的c_i最小值
    void calc(int x)
    {
        val=len&1?min(mn[x][0],mn[x^1][1]):0;
    }
}o[maxn];
int find(int x)
{
    if(f[x]==x) return x;
    return f[x]=find(f[x]);
}
void chmin(int &x,int y)
{
    if(x>=y) x=y;
}
vector<ll> calculate_costs(vi w,vi a,vi b,vi e)
{
    int n=w.size(),q=e.size();
    ll res=0;
    for(int i=0;i<n;i++) p[i]=mp(w[i],a[i]-b[i]),res+=a[i];
    for(int i=0;i<q;i++) d[i]=mp(e[i],i);
    sort(p,p+n),sort(d,d+q);
    for(int i=0;i<n;i++) f[i]=i,w[i]=p[i].fi,o[i].len=1,o[i].mn[i&1][0]=o[i].val=p[i].se;
    for(int i=0;i<n;i++) r[i]=mp(i?w[i]-w[i-1]:inf,i),s[i]=mp(i&&i!=n-1?w[i+1]-w[i-1]:inf,i);
    sort(r,r+n),sort(s,s+n);
    vector<ll> vec(q);
    for(int i=0,j=0,k=0;i<q;i++)
    {
        while(j<n&&r[j].fi<=d[i].fi)
        {
            int v=r[j].se,u=find(v-1);
            res-=o[u].val+o[v].val,f[v]=u,o[u].len+=o[v].len;
            for(int x=0;x<=1;x++)
                for(int y=0;y<=1;y++)
                    chmin(o[u].mn[x][y],o[v].mn[x][y]);
            o[u].calc(u&1),res+=o[u].val,j++;
        }
        while(k<n&&s[k].fi<=d[i].fi)
        {
            int v=s[k].se,u=find(v);
            res-=o[u].val;
            chmin(o[u].mn[v&1][1],p[v].se);
            o[u].calc(u&1),res+=o[u].val,k++;
        }
        vec[d[i].se]=res;
    }
    return vec;
}
posted @ 2024-09-08 17:16  peiwenjun  阅读(32)  评论(0编辑  收藏  举报