尼罗河题解
题目传送门
刚看到这题的时候还是紫,过了二十分钟降蓝了。
考虑
注意到一个性质:如果把所有两两配对的货物视为一条线段,在最优情况下这些线段一定能够不相交。假设两两有相交,将四个点中左边两个和右边两个连一起显然合法且等价。
注意到另一个性质:如果把所有两两配对的货物视为一条线段,在最优情况下这些线段长度不会超过
于是递推式:
考虑优化。注意到询问可以离线出来,按照
这样转移的限制越来越宽,每个位置的转移式子也会变化。但是这些变化的总数是
考虑维护每个式子变化的时间。把每个
我们把转移抽象成广义矩阵乘法。其中把乘法操作变为加法,加法操作变为取
matrix operator * (matrix a,matrix b){
matrix ans;
ans.init1(3,3);
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
for(int k=0;k<3;k++){
ans.a[i][j]=min(ans.a[i][j],a.a[i][k]+b.a[k][j]);
}
}
}
return ans;
}
因为需要考虑前两个位置,矩阵边长为
三个转移对应的矩阵乘分别为:
能够证明这种运算具有结合率。
线段树维护每个式子,单点修改
细节见代码部分。
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 100100;
struct matrix{
int n,m;
long long a[3][3];
inline void init0(int x,int y){
n=x,m=y;
for(int i=0;i<n;i++) for(int j=0;j<m;j++) a[i][j]=0;
return;
}
inline void init1(int x,int y){
n=x,m=y;
for(int i=0;i<n;i++) for(int j=0;j<m;j++){
a[i][j]=1e18;
}
return;
}
inline void print(){
for(int i=0;i<n;i++){
for(int j=0;j<m;j++) cout<<a[i][j]<<' ';
cout<<'\n';
}
}
};
matrix operator * (matrix a,matrix b){
matrix ans;
ans.init1(3,3);
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
for(int k=0;k<3;k++){
ans.a[i][j]=min(ans.a[i][j],a.a[i][k]+b.a[k][j]);
}
}
}
return ans;
}
struct node{
int w,a,b;
}p[N];
struct ask{
int id,d;
}e[N];
int ans[N];
int n,q;
vector <long long> res;
long long w[N],a[N],b[N];
bool cmp(ask a,ask b){
return a.d<b.d;
}
matrix tree[N*4];
inline void push_up(int x){
tree[x]=tree[x*2+1]*tree[x*2];
}
int f1,f2;
inline void build(int l,int r,int x){
if(l==r){
tree[x].init1(3,3);
tree[x].a[0][0]=a[l];
tree[x].a[1][0]=0;
tree[x].a[2][1]=0;
return;
}
int mid=l+r>>1;
build(l,mid,x*2);
build(mid+1,r,x*2+1);
push_up(x);
return;
}
matrix d;
inline void updata(int l,int r,int id,int x){
if(l>id || r<id) return;
if(l>=id && r<=id){
if(l==2){
tree[x].a[0][0]=f2;
tree[x].a[1][0]=f1;
tree[x].a[2][0]=0;
return;
}
tree[x]=d;
return;
}
int mid=l+r>>1;
if(mid>=id) updata(l,mid,id,x*2);
if(mid+1<=id) updata(mid+1,r,id,x*2+1);
if(l==1 && r==2) tree[x]=tree[x*2+1]; else push_up(x);
return;
}
bool cmp0(node a,node b){
return a.w<b.w;
}
vector <pair <int,int> > v1,v2;
std::vector<long long> calculate_costs(
std::vector<signed> W, std::vector<signed> A,
std::vector<signed> B, std::vector<signed> E){
n=W.size(),q=E.size();
for(int i=1;i<=n;i++) p[i].a=A[i-1],p[i].b=B[i-1],p[i].w=W[i-1];
sort(p+1,p+1+n,cmp0);
for(int i=2;i<=n;i++) v1.push_back({p[i].w-p[i-1].w,i});
for(int i=3;i<=n;i++) v2.push_back({p[i].w-p[i-2].w,i});
for(int i=1;i<=n;i++) a[i]=p[i].a,b[i]=p[i].b,w[i]=p[i].w;
for(int i=1;i<=q;i++) e[i].d=E[i-1],e[i].id=i;
sort(e+1,e+1+q,cmp);
sort(v1.begin(),v1.end()),sort(v2.begin(),v2.end());
build(1,n,1);
int x=0,y=0;
for(int i=1;i<=q;i++){
f1=a[1];
if(w[2]-w[1]<=e[i].d) f2=b[1]+b[2];
else f2=a[1]+a[2];
updata(1,n,2,1);
d.init1(3,3);
d.a[1][0]=0;
d.a[2][1]=0;
while(x<n-1 && v1[x].first<=e[i].d){
int j=v1[x].second;
d.a[0][0]=a[j];
d.a[0][1]=b[j]+b[j-1];
updata(1,n,v1[x].second,1),++x;
}
while(y<n-1 && v2[y].first<=e[i].d){
int j=v2[y].second;
d.a[0][0]=a[j];
d.a[0][1]=b[j]+b[j-1];
d.a[0][2]=b[j]+b[j-2]+a[j-1];
updata(1,n,v2[y].second,1),++y;
}
ans[e[i].id]=tree[1].a[0][0];
}
for(int i=1;i<=q;i++) res.push_back(ans[i]);
return res;
}