题解 P3580 【[POI2014]ZAL-Freight】
xtx1092515503 · · 题解
个人DP时想傻掉了,但是最终还是用奇妙的方式实现了这个傻掉的做法,因此发一篇题解。
首先,状态非常好设,
本质上是一个优化DP的过程。
我们考虑最暴力的
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,a[1001000];
ll f[1001000];
int main(){
scanf("%d%d",&n,&m),memset(f,0x3f,sizeof(f)),a[0]=-1;
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
f[0]=0;
for(int i=0;i<n;i++){
ll now=f[i]-1;
for(int j=i+1;j<=n;j++){
now++,now=max(now,1ll*a[j]);
f[j]=min(f[j],now+2*m+(j-i-1));
}
}
printf("%lld\n",f[n]);
return 0;
}
但是,这个DP非常令人不爽,因为我们没有办法通过什么式子直接表示出
这时就要祭出一个不怎么常见的应用于题目中所述的递增序列的小trick了:
如果一个序列
我们考虑将其应用于上述DP:
序列
减一下就发现,序列
于是我们只需要先把整个序列转成上述
于是我们现在便可以有一个DP的式子了:
虽然还是
此部分代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,a[1001000];
ll f[1001000];
int main(){
scanf("%d%d",&n,&m),memset(f,0x3f,sizeof(f));
for(int i=1;i<=n;i++)scanf("%d",&a[i]),a[i]-=i;
f[0]=0;
for(int i=0;i<n;i++){
ll now=f[i]-i-1;
// printf("%d\n",now);
for(int j=i+1;j<=n;j++){
now=max(now,1ll*a[j]);
// printf("%d->%d:%d\n",i,j,now);
f[j]=min(f[j],now+j+2*m+(j-i-1));
}
}
// for(int i=0;i<=n;i++)printf("%lld ",f[i]);puts("");
printf("%lld\n",f[n]);
return 0;
}
现在考虑上奇怪的东西来维护了。
我们翻出式子
提取一些东西出来
然后里面的东西,我们可以分情况讨论。
-
f_i-i-1\geq\max\{a_{i+1},\dots,a_j\}
首先,
所以我们便可以用two-pointers维护第一个不满足上述条件的
考虑随着
-
f_i-i-1<\max\{a_{i+1},\dots,a_j\}
此处有
考虑位置
特别地,在
考虑在
我们考虑对于位置
故我们可以无条件把前面继承过来的东西换成位置
则时间复杂度
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,a[1001000],g[1001000],nowa,nowb=-1;
ll f[1001000];
deque<pair<ll,int> >q;
int main(){
scanf("%d%d",&n,&m),memset(f,0x3f,sizeof(f)),memset(g,-1,sizeof(g));
for(int i=1;i<=n;i++)scanf("%d",&a[i]),a[i]-=i;
// for(int i=1;i<=n;i++)printf("%d ",a[i]);puts("");
f[0]=-2*m+1;
for(int i=0,j=1;i<=n;i++){
while(!q.empty()&&q.front().second<=i)q.pop_front();
if(!q.empty())f[i]=min(f[i],q.front().first);
if(g[i]!=-1)nowa=a[i],nowb=g[i];
nowa=max(nowa,a[i]);
if(nowb!=-1)f[i]=min(f[i],1ll*nowa-nowb);
f[i]+=2*(i+m)-1;
ll now=f[i]-i-1;
for(j=max(i+1,j);j<=n&&now>=a[j];j++);
// printf("%d:%d\n",now,j);
while(!q.empty()&&q.back().first>=now-i)q.pop_back();
q.push_back(make_pair(now-i,j));
g[j]=max(g[j],i);
}
// for(int i=0;i<=n;i++)printf("%lld ",f[i]);puts("");
printf("%lld\n",f[n]);
return 0;
}