题解:P11049 [IOI 2024] 尼罗河船运

题目传送门

先考虑暴力,容易想到动态规划,先按重量从小到大排序,\(dp_i\) 表示前 \(i\) 个运过去的最小代价,容易得到一下转移式:

\[dp_i=\begin{cases} dp_{i-1}+a_i & w_i-w_{i-1}>d \\ \min(dp_{i-1}+a_i,dp_{i-2}+b_{i-1}+b_i) & w_i-w_{i-1}\le d \\ \min(dp_{i-1}+a_i,dp_{i-2}+b_{i-1}+b_i,dp_{i-3}+b_{i-2}+b_i+a_{i-1}) & w_i-w_{i-2}\le d \end{cases}\]

解释一下,第一行是第 \(i\) 个货物自己一条船,第二行是 \(i\)\(i-1\) 一起运过去,第三行是 \(i\)\(i-2\) 一起运过去,\(i-1\) 自己过去。那为什么能这么判断,因为假设有 \(4\) 个货物,重量分别为 \(w_1,w_2,w_3,w_4\),有 \(w_1<w_2<w_3<w_4\),那么如果 \(1\)\(4\) 一起运过去,一定不优,因为可以 \(1\)\(2\) 一起,\(3\)\(4\) 一起。所以对于每一个货物,只需要和前面 \(2\) 个比较就可以了。

这个代码复杂度是 \(O(nq)\) 的,可以拿到 \(67\) 分。

考虑如何优化,想到可以把询问排序,离线做。把询问从小到大排,容易发现因为限制越来越宽,所以当 \(d\) 足够大时,答案固定。所以我们维护每个 \(i\) 的三条式子的变化,最初始时我们认为每个 \(i\) 仅满足第一条式子。把 \(w_i\) 从小到大排,\(w_i-w_{i-1}\)\(w_i-w_{i-2}\) 也从小到大排,为了能快速的维护变化。

我们考虑用线段树维护矩阵乘法来实现,这里要定义一个广义矩阵,即把原来矩阵乘法的第 \(i\)\(\times\)\(j\) 列再求和,变为第 \(i\)\(+\)\(j\) 列再取 \(\min\)

细节如下:

for(int i=1;i<=3;i++)
    for(int j=1;j<=3;j++)
        for(int k=1;k<=3;k++)
            res.a[i][j]=min(res.a[i][j],a[i][k]+b.a[k][j]);

考虑用矩阵实现状态转移:

\[\begin{bmatrix} a_i & +\infty & +\infty \\ 0 & +\infty & +\infty \\ +\infty & 0 & +\infty \end{bmatrix} \begin{bmatrix} dp_{i-1} \\ dp_{i-2} \\ dp_{i-3} \end{bmatrix} = \begin{bmatrix} dp_i \\ dp_{i-1} \\ dp_{i-2} \end{bmatrix} \]

\[\begin{bmatrix} a_i & b_i+b_{i-1} & +\infty \\ 0 & +\infty & +\infty \\ +\infty & 0 & +\infty \end{bmatrix} \begin{bmatrix} dp_{i-1} \\ dp_{i-2} \\ dp_{i-3} \end{bmatrix} = \begin{bmatrix} dp_i \\ dp_{i-1} \\ dp_{i-2} \end{bmatrix} \]

\[\begin{bmatrix} a_i & b_i+b_{i-1} & b_i+a_{i-1}+b_{i-2} \\ 0 & +\infty & +\infty \\ +\infty & 0 & +\infty \end{bmatrix} \begin{bmatrix} dp_{i-1} \\ dp_{i-2} \\ dp_{i-3} \end{bmatrix} = \begin{bmatrix} dp_i \\ dp_{i-1} \\ dp_{i-2} \end{bmatrix} \]

同正常的矩阵乘法一样,这个也具有结合律。那么我们现在只需要用线段树维护每个式子,单点进行修改,最后根节点为答案。

注意矩阵乘法的顺序,以及 \([1,1]\) 的叶子结点的矩阵为空,push_up 时注意判断,注意初始矩阵中的每一个数为无穷大。

代码如下:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1010101;
const int M=4;
struct tt{
	ll a[M][M];
	void init(){memset(a,0x3f,sizeof(a));}
	tt operator*(const tt &b)const{
		tt res;
		res.init();
		for(int i=1;i<=3;i++)
			for(int j=1;j<=3;j++)
				for(int k=1;k<=3;k++)
					res.a[i][j]=min(res.a[i][j],a[i][k]+b.a[k][j]);
		return res;
	}
}C,t[N];
struct node{ll w,a,b;}s[N];
bool cmp(node a,node b){return a.w<b.w;}
ll f1,f2;
vector<pair<ll,ll> >e,v1,v2;
vector<ll>num;
ll ans[N];
ll ls(ll p){return p<<1;}
ll rs(ll p){return p<<1|1;}
void push_up(ll p){t[p]=t[rs(p)]*t[ls(p)];}
void build(ll p,ll l,ll r){
	if(l>r)return;
	if(l==r){
		t[p]=C;
		t[p].a[1][1]=s[l].a;
		t[p].a[2][1]=t[p].a[3][2]=0;
		return;
	}
	ll mid=l+r>>1;
	build(ls(p),l,mid);
	build(rs(p),mid+1,r);
	if(l==1&&r==2)t[p]=t[rs(p)];
	else push_up(p);
}
void update(ll p,ll l,ll r,ll x,ll idx){
	if(x<l||r<x)return;
	if(l==r&&l==x){
		if(l==2)t[p].a[1][1]=f2,t[p].a[2][1]=f1,t[p].a[3][1]=0;
		else{
			if(idx==1)t[p].a[1][2]=s[l].b+s[l-1].b;
			if(idx==2)t[p].a[1][3]=s[l].b+s[l-2].b+s[l-1].a;
		}
		return ;
	}
	ll mid=l+r>>1;
	update(ls(p),l,mid,x,idx);
	update(rs(p),mid+1,r,x,idx);
	if(l==1&&r==2)t[p]=t[rs(p)];
	else push_up(p);
}
vector<long long> calculate_costs(vector<int> W,vector<int>A,vector<int>B,vector<int>E){
	ll n=W.size(),m=E.size();
	for(int i=1;i<=n;i++)s[i].w=W[i-1],s[i].a=A[i-1],s[i].b=B[i-1];
	sort(s+1,s+n+1,cmp);
	for(int i=1;i<=m;i++)e.push_back({E[i-1],i});
	for(int i=2;i<=n;i++)v1.push_back({s[i].w-s[i-1].w,i});
	for(int i=3;i<=n;i++)v2.push_back({s[i].w-s[i-2].w,i});
	sort(e.begin(),e.end());
	sort(v1.begin(),v1.end());
	sort(v2.begin(),v2.end());
	C.init();
	build(1,1,n);
	ll i=0,j=0;
	for(auto u:e){
		f1=s[1].a;
		if(s[2].w-s[1].w<=u.first)f2=s[1].b+s[2].b;
		else f2=s[1].a+s[2].a;
		update(1,1,n,2,0);
		while(i<n-1&&v1[i].first<=u.first)update(1,1,n,v1[i].second,1),i++;
		while(j<n-1&&v2[j].first<=u.first)update(1,1,n,v2[j].second,2),j++;
		ans[u.second]=t[1].a[1][1];
	}
	for(int i=1;i<=m;i++)num.push_back(ans[i]);
	return num;
}
// int main(){
// 	vector<ll>res;
// 	res=calculate_costs({15, 12, 2, 10, 21},{5, 4, 5, 6, 3},{1, 2, 2, 3, 2},{5, 9, 1});
// 	for(auto i:res)cout<<i<<"\n";
// 	return 0;
// }
posted @ 2025-09-10 21:38  一班的hoko  阅读(11)  评论(0)    收藏  举报