题解:P11142 [APC001] Ex - Separation
lailai0916 · · 题解
题意简述
数轴上有若干工厂,货物会在给定时刻产生。每次运输者都从
运输者可以在一次出发时制造一个分身,但所有人共用体力。要求最小化货物从产生到运抵
解题思路
一次完整往返的路程恒为
若存在货物而
考虑位于
若某次运输在时刻
货物在
其中
接下来只需最小化所有货物的
把所有
若一段的右端点为
把一个非空段拆成相邻的两个非空段。让两次运输分别在各自右端点的
设前缀和为
若最后一段为
所以转移为:
整理与
把每个决策点
判断直线是否无用时,直接比较交点的先后关系。交叉乘积使用 __int128 计算,避免浮点误差和整数溢出。DP 只需保存相邻两层。另用
倒推得到的每个右端点对应一次出发,其绝对出发时刻就是该端点的
最后确定每次出发由谁完成。用小根堆维护所有已经出现的人的返家时刻,并先放入一个负无穷表示本体。按时间顺序处理出发事件:若最早返家的人已经可用,就让他再次出发;否则本次制造一个分身。每次出发至多制造一个分身,恰好符合题意。
还需说明此构造始终满足共享体力限制。开始第
所以任意时刻剩余体力都足以让所有在途人员回家。
时间复杂度为
参考代码
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
__extension__ using i128=__int128;
const int N=200005;
const int K=105;
const ll inf=4000000000000000000LL;
struct Line
{
int k;
ll b;
}h[N];
int a[N],b[N],pre[K][N];
ll s[N],sum[N],f[2][N];
ll get(Line z,ll x)
{
return z.b-z.k*x;
}
bool bad(Line u,Line v,Line w)
{
return (i128)(v.b-u.b)*(w.k-v.k)>=(i128)(w.b-v.b)*(v.k-u.k);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin>>t;
while(t--)
{
int n,m,x,c,k;
cin>>n>>m>>x>>c>>k;
for(int i=1;i<=n;i++)cin>>a[i];
int cnt=0;
ll ans=0;
for(int i=1;i<=n;i++)
{
cin>>b[i];
cnt+=b[i];
ans+=(ll)b[i]*m*(x-a[i]);
}
int len=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=b[i];j++)
{
int v;
cin>>v;
s[++len]=v-a[i];
}
}
if(!cnt)
{
cout<<0<<'\n';
cout<<-1<<' '<<-1<<'\n';
continue;
}
int r=c/(2*x);
if(!r)
{
cout<<-1<<'\n';
continue;
}
r=min(r,cnt);
sort(s+1,s+cnt+1);
for(int i=1;i<=cnt;i++)sum[i]=sum[i-1]+s[i];
f[0][0]=0;
fill(f[0]+1,f[0]+cnt+1,inf);
for(int i=1;i<=r;i++)
{
int now=(i-1)&1,nxt=i&1;
int head=0,tail=0;
h[0]={i-1,f[now][i-1]+sum[i-1]};
for(int j=i;j<=cnt;j++)
{
while(head<tail&&get(h[head],s[j])>=get(h[head+1],s[j]))head++;
int p=h[head].k;
f[nxt][j]=f[now][p]+(j-p)*s[j]-sum[j]+sum[p];
pre[i][j]=p;
if(f[now][j]>=inf/2)continue;
Line z={j,f[now][j]+sum[j]};
while(head<tail&&bad(h[tail-1],h[tail],z))tail--;
h[++tail]=z;
}
}
ans+=f[r&1][cnt]*m;
cout<<ans<<'\n';
vector<ll> dep;
int pos=cnt;
for(int i=r;i;i--)
{
dep.push_back(s[pos]);
pos=pre[i][pos];
}
reverse(dep.begin(),dep.end());
priority_queue<ll,vector<ll>,greater<ll>> q;
q.push(-inf);
for(ll v:dep)
{
int clone=1;
if(q.top()<=v)
{
clone=0;
q.pop();
}
cout<<v-k<<' '<<clone<<'\n';
q.push(v+2*x);
}
cout<<-1<<' '<<-1<<'\n';
}
return 0;
}