题目描述:
在遥远的精灵世界,有一个神奇的国度。这个国度由N座城市(从1到N编号)构成,其中1号城市是精灵们的首都,N号城市是精灵们的边疆。精灵国王每年都要从首都出发到边疆去巡游,沿途抚恤她的子民们:她每到一个城市i,就会花A[i]天的时间来巡游这个城市。
精灵世界分布在不同的次元中,所以精灵们只能通过传送阵出行。他们每个城市有一个空间坐标,第i座城市的坐标为P[i],保证P[i]是[1,M]之间的正整数。传送阵的传送距离和传送效率是有限的,精灵国王每次可以从第i座城市出发,到达编号在[i+1,i+k]之间的城市j,并花费|P[i]-P[j]|*B[i]天的时间。
精灵国王想设计一个最优的从1到n的巡游方案,使得她在传送中花费的时间加上巡游花费的时间最少(她也会巡游首都和边疆这两座城市),你能帮帮她吗?
n<=50000;
解题思路:
很容易列出
O(n2)
O
(
n
2
)
的dp方程:
假设没有k的限制,那
pj
p
j
的影响可以根据
pi,pj
p
i
,
p
j
的大小关系讨论。
当
pi≥pj,fi=min(bjpi+fj−bjpj)
p
i
≥
p
j
,
f
i
=
m
i
n
(
b
j
p
i
+
f
j
−
b
j
p
j
)
,可以看做是一条覆盖了
(pj,m)
(
p
j
,
m
)
,斜率为
bj
b
j
,截距为
fj−bjpj
f
j
−
b
j
p
j
的线段。
那么问题就变为了有一些线段,求 x=pi x = p i 取到的最小值,这个问题可以参考bzoj3165,用线段树维护至 O(log2n) O ( l o g 2 n )
现在有了
k
k
的限制,我们只需把限制和询问都放到一棵外层的线段树上,遍历线段树每个节点时都重建上面提到的线段树处理更新该节点的询问的答案即可。
时间复杂度是
还有做法是用同样的线段树分治法维护斜率dp的凸包,时间复杂度 O(nlog2n) O ( n l o g 2 n ) 。
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
using namespace std;
int getint()
{
int i=0,f=1;char c;
for(c=getchar();c!='-'&&(c<'0'||c>'9');c=getchar());
if(c=='-')f=-1,c=getchar();
for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
const int N=2000005;
const ll INF=1e18;
int n,m,k;
int p[N],a[N],b[N];
ll f[N],K[N],B[N];
bool exist[N];
vector<int>upt[N],q[N];
void Add_modify(int k,int l,int r,int x,int y,int id)
{
if(x>y)return;
if(x<=l&&r<=y){upt[k].pb(id);return;}
int mid=l+r>>1;
if(x<=mid)Add_modify(k<<1,l,mid,x,y,id);
if(y>mid)Add_modify(k<<1|1,mid+1,r,x,y,id);
}
void Add_query(int k,int l,int r,int x)
{
q[k].pb(x);if(l==r)return;
int mid=l+r>>1;
if(x<=mid)Add_query(k<<1,l,mid,x);
else Add_query(k<<1|1,mid+1,r,x);
}
void newnode(int k){exist[k]=1,K[k]=0,B[k]=INF;}
void del(int k)
{
if(!exist[k])return;
exist[k]=false;
del(k<<1),del(k<<1|1);
}
ll calc(ll k,ll b,ll x){return k*x+b;}
void update(int k,int l,int r,ll k2,ll b2)
{
ll k1=K[k],b1=B[k];
if(calc(k1,b1,l)>=calc(k2,b2,l))swap(k1,k2),swap(b1,b2);
if(calc(k1,b1,r)<=calc(k2,b2,r)){K[k]=k1,B[k]=b1;return;}
int mid=l+r>>1;
if(calc(k1,b1,mid)<=calc(k2,b2,mid))
K[k]=k1,B[k]=b1,update(k<<1|1,mid+1,r,k2,b2);
else K[k]=k2,B[k]=b2,update(k<<1,l,mid,k1,b1);
}
void modify(int k,int l,int r,int x,int y,ll ki,ll bi)
{
if(!exist[k])newnode(k);
if(x<=l&&r<=y){update(k,l,r,ki,bi);return;}
int mid=l+r>>1;
if(x<=mid)modify(k<<1,l,mid,x,y,ki,bi);
if(y>mid)modify(k<<1|1,mid+1,r,x,y,ki,bi);
}
ll query(int k,int l,int r,int x)
{
if(!exist[k])return INF;
ll res=calc(K[k],B[k],x);
if(l==r)return res;
int mid=l+r>>1;
if(x<=mid)res=min(res,query(k<<1,l,mid,x));
else if(x>mid)res=min(res,query(k<<1|1,mid+1,r,x));
return res;
}
void solve(int k,int l,int r)
{
if(!q[k].size())return;
for(int i=0;i<upt[k].size();i++)
{
int j=upt[k][i];
modify(1,1,m,p[j],m,b[j],f[j]+a[j]-1ll*b[j]*p[j]);
modify(1,1,m,1,p[j],-b[j],f[j]+a[j]+1ll*b[j]*p[j]);
}
for(int i=0;i<q[k].size();i++)f[q[k][i]]=min(f[q[k][i]],query(1,1,m,p[q[k][i]]));
del(1);int mid=l+r>>1;
solve(k<<1,l,mid),solve(k<<1|1,mid+1,r);
}
int main()
{
//freopen("lx.in","r",stdin);
n=getint(),m=getint(),k=getint();
for(int i=1;i<=n;i++)p[i]=getint();
for(int i=1;i<=n;i++)a[i]=getint();
for(int i=1;i<=n;i++)b[i]=getint();
for(int i=1;i<=n;i++)
{
Add_modify(1,1,n,i+1,min(n,i+k),i);
if(i!=1)f[i]=INF,Add_query(1,1,n,i);
}
solve(1,1,n);
printf("%lld\n",f[n]+a[n]);
return 0;
}