传送门
思路还不错的一道题目,难度不大,但是我在写的时候出了一些细节上的错误
显然题目中的两问都可以通过DP来解决
f[i]
表示保留旧房子
i
时,
g[i]
表示
f[i]
为最优时,
1~i
的最小总花费
f[i]=max{f[j]+1}(j=0..i−1,a[i]−a[j]≥i−j)
g[i]=min{(2∗a[j]+i−j)∗(i−j−1)2+g[j]}+a[i]+b[i]
,其中
f[j]
必须是
f[i]
的最优决策点
O(n2)
的复杂度显而易见,我们来考虑优化
对于
f
的优化非常显然,分治或树状数组什么的怎样都能做,但
把
a[i]+b[i]
拿掉,我们把
g[i]
变成这个样子
i∗(a[j]−j)+i∗(i−1)2+(j(j+1)2−j∗a[j]−a[j]+g[j])
这样
g[i]
就变成了一个非常传统的斜率优化DP的形式了,把
i∗(i−1)2
拿掉,每次插入一个点
(a[j]−j,j(j+1)2−j∗a[j]−a[j]+g[j])
,转移时拿斜率为
−i
的直线去卡最小截距就可以了
理论问题解决了,那怎么实现呢
我们可以分治一发,处理左半边对右半边的影响时按照
a[i]−i
排序,拿个splay或set维护一下凸包,查询时三分就可以了
……
可以写写试试,不过常数巨大,我们完全没必要这么做,因为观察插入点的横坐标也是
a[j]−j
,也就是说排序后插入的点也是单调的,开个vector从末尾存一下,维护下凸壳就行了
(这里我也是蠢,好久都没发现插入点是单调的,差点写了cdq套set)
最终时间复杂度
O(nlog2n)
,但要说几个容易错的地方,比如long long,比如转移要从0开始,比如f,g转移时的优先顺序等等,还有一点我没注意到,但因为数据的原因只WA了一个点,就是f在转移时要注意决策点本身必须保证有解,也就是说如果决策点不是0,那么它的
f
是必须大于0的
还有就是注意按
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
#define LL long long
#define M 100005
#define mp make_pair
using namespace std;
int in()
{
int t=0;char ch=getchar();
while (ch>'9'||ch<'0') ch=getchar();
while (ch>='0'&&ch<='9') t=(t<<1)+(t<<3)+ch-48,ch=getchar();
return t;
}
int n;
int a[M],b[M],f[M],id[M];
LL g[M];
typedef pair<int,LL>node;
vector<node> v;
bool cmp(int x,int y)
{
if (a[x]-x==a[y]-y) return x<y;
return a[x]-x<a[y]-y;
}
node operator -(node a,node b){return mp(a.first-b.first,a.second-b.second);}
LL operator *(node a,node b){return a.first*b.second-a.second*b.first;}
void solve(int l,int r)
{
if (l==r) return;
int mid=l+r>>1,cnt=0;
solve(l,mid);
for (int i=l;i<=r;++i) id[++cnt]=i;
sort(id+1,id+cnt+1,cmp);
node now;
int mx=-1,L,R,mid1,mid2;
for (int i=1;i<=cnt;++i)
if (id[i]<=mid)
{
if (f[id[i]]<mx) continue;
if (id[i]!=0&&!f[id[i]]) continue;
if (f[id[i]]>mx)
v.clear(),
mx=f[id[i]];
now=mp(2*a[id[i]]-2*id[i],1LL*id[i]*(id[i]+1)-1LL*2*id[i]*a[id[i]]-2*a[id[i]]+2*g[id[i]]);
while (v.size()>1&&(v[v.size()-1]-v[v.size()-2])*(now-v[v.size()-1])<=0) v.pop_back();
v.push_back(now);
}
else
{
if (f[id[i]]>mx+1) continue;
if (f[id[i]]<mx+1) g[id[i]]=1e18;
f[id[i]]=mx+1;
L=0,R=v.size()-1;
for (;R-L>2;)
{
mid1=(R-L)/3+L;
mid2=(R-L)/3*2+L;
if (1LL*v[mid1].first*id[i]+v[mid1].second>1LL*v[mid2].first*id[i]+v[mid2].second) L=mid1+1;
else R=mid2-1;
}
for (int j=L;j<=R;++j)
g[id[i]]=min(g[id[i]],(1LL*v[j].first*id[i]+1LL*id[i]*(id[i]-1)+v[j].second)/2+a[id[i]]+b[id[i]]);
}
solve(mid+1,r);
}
main()
{
n=in();
for (int i=1;i<=n;++i) a[i]=in();
for (int i=1;i<=n;++i) b[i]=in();
for (int i=1;i<=n;++i) g[i]=1e18;
solve(0,n);
int maxn=0;
LL ans;
for (int i=1;i<=n;++i)
{
g[i]+=1LL*(n-i)*(a[i]+1)+1LL*(n-i)*(n-i-1)/2;
if (maxn<f[i]) maxn=f[i],ans=g[i];
else if (maxn==f[i]) ans=min(ans,g[i]);
}
printf("%d %lld\n",maxn,ans);
}