题目描述
有 \(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};
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;
}
标签:le,下标,int,题解,maxn,IOI2024,vi,LOJ4218,define
From: https://www.cnblogs.com/peiwenjun/p/18403147